emplace_back not working with std::vector>

后端 未结 3 1926
我寻月下人不归
我寻月下人不归 2021-01-17 08:00

I am trying to do emplace_back into a std::vector>, but could not find the right syntax to do it.

#i         


        
相关标签:
3条回答
  • 2021-01-17 08:04

    This seems to be currently unsupported, if I understand these issue reports right:

    http://cplusplus.github.io/LWG/lwg-active.html#2089

    http://cplusplus.github.io/LWG/lwg-active.html#2070

    0 讨论(0)
  • 2021-01-17 08:16

    One can achieve that using a helper function as follows:

     #include <map>
     #include <vector>
    
     void emplace_work_around(
        std::vector<std::map<int, int>>& v,
        std::initializer_list<std::pair<const int,int>> && item
     )
     {
        v.emplace_back(std::forward<std::initializer_list<std::pair<const int,int>>>(item));
     }
    
    int main()
    {
        std::vector<std::map<int, int>> v;
    
        emplace_work_around(v,{{1,2}});
    }
    

    The problem was when we write:

    v.emplace_back({{1,2}});  // here {{1,2}} does not have a type.
    

    the compiler is not able to deduce the type of the argument, and it can't decide which constructor to call.

    The underlying idea is that when you write a function like

    template<typename T>
    void f(T) {}
    

    and use it like

    f( {1,2,3,4} ); //error
    

    you will get compiler error, as {1,2,3,4} does have a type.

    But if you define your function as

    template<typename T>
    void f(std::initializer_list<T>) {}
     f( {1,2,3,4} );
    

    then it compiles perfectly.

    0 讨论(0)
  • 2021-01-17 08:23

    emplace_back does forward all arguments to a matching constructor of the member type. Now, std::map has a initializer-list constructor, but it expects a list of std::pair<const Key, Value>, i.e. std::pair<const int, int>. push_back is not a template, so it just expects one type and thus performs the conversion in place. That is, no type-deduction occurs here.

    You would need to explicitly state that you want to have a std::pair; the following should work:

    #include<map>
    #include<vector>
    
    int main()
    {
        std::vector<std::map<int, int>> v;
    
        v.emplace_back(std::initializer_list<std::pair<const int, int>>{
                {1,2},{3,4},{5,6}});
    
        return 0;
    }
    

    For the same reason, this does not compile:

        v.emplace_back({std::pair<const int,int>(1,2),
                        std::pair<const int,int>(3,4)});
    

    This is because, though a brace-enclosed list may yield an initializer-list, it doesn't have to. It can also be a constructor call or something like that. So, writing

    auto l = {std::pair<const int,int>(1,2),
              std::pair<const int,int>(3,4)};
    

    yields an initializer list for l, but the expression itself might be used in another way:

    std::pair<std::pair<const int, int>, std::pair<const int, int>> p =
              {std::pair<const int,int>(1,2),
              std::pair<const int,int>(3,4)}
    

    This whole stuff gets a bit messy.

    Basically, if you have an brace-enclosed-list, it may yield an initializer list or call a matching constructor. There are cases where the compiler is not able to determine which types are needed; emplace_back is one of them (because of forwarding). In other cases it does work, because all types are defined in the expression. E.g.:

    #include <vector>
    #include <utility>
    
    int main() 
    {
        std::vector<std::pair<const int, int>> v = 
             {{1,2},{3,4},{5,6}};
        return 0;
    }
    

    Now the reason it doesn't work is that no type can be deduced. I.e. emplace_back tries to deduce the name of the input types, but this is not possible, since a brace-enclosed-list has several types it can describe. Hence there is not a matching function call.

    0 讨论(0)
提交回复
热议问题