I am trying to do emplace_back
into a std::vector
, but could not find the right syntax to do it.
#i
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
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.
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.