简体   繁体   中英

Emplacement of a vector with initializer list

i have a std::vector<std::vector<double>> and would like to add some elements at the end of it so this was my trial:

std::vector<std::vector<double> > vec;
vec.emplace_back({0,0});

but this does not compile whereas the following will do:

std::vector<double> vector({0,0});

Why can't emplace_back construct the element at this position? Or what am i doing wrong?

Thanks for your help.

The previous answer mentioned you could get the code to compile when you construct the vector in line and emplace that. That means, however, that you are calling the move-constructor on a temporary vector, which means you are not constructing the vector in-place, while that's the whole reason of using emplace_back rather than push_back .

Instead you should cast the initializer list to an initializer_list , like so:

#include <vector>
#include <initializer_list>

int main()
{
    std::vector<std::vector<int>> vec;
    vec.emplace_back((std::initializer_list<int>){1,2});
}

Template deduction cannot guess that your brace-enclosed initialization list should be a vector. You need to be explicit:

vec.emplace_back(std::vector<double>{0.,0.});

Note that this constructs a vector, and then moves it into the new element using std::vector 's move copy constructor. So in this particular case it has no advantage over push_back() . @TimKuipers 's answer shows a way to get around this issue.

There are really two issues here: the numeric type conversion and template argument deduction.

The std::vector<double> constructor is allowed to use the braced list (even of int s) for its std::vector<double>(std::initializer_list<double>) constructor. (Note, however, that std::initializer_list<int> does not implicitly convert to std::initializer_list<double> )

emplace_back() cannot construct the element from the brace expression because it is a template that uses perfect forwarding. The Standard forbids the compiler to deduce the type of {0,0} , and so std::vector<double>::emplace_back<std::initializer_list<double>>(std::initializer_list<double>) does not get compiled for emplace_back({}) .

Other answers point out that emplace_back can be compiled for an argument of type std::initializer_list<vector::value_type> , but will not deduce this type directly from a {} expression.

As an alternative to casting the argument to emplace_back , you could construct the argument first. As pointed out in Meyers' Item 30 (Effective Modern C++), auto is allowed to deduce the type of a brace expression to std::initializer_list<T> , and perfect forwarding is allowed to deduce the type of an object whose type was deduced by auto .

std::vector<std::vector<double> > vec;
auto double_list = {0., 0.}; // int_list is type std::initializer_list<int>
vec.emplace_back(double_list); // instantiates vec.emplace_back<std::initializer_list<double>&>

emplace_back adds an element to vec by calling std::vector<double>(std::forward<std::initializer_list<double>>(double_list)) , which triggers the std::vector<double>(std::initializer_list<double>) constructor.

Reference: Section 17.8.2.5 item 5.6 indicates that the this is a non-deduced context for the purposes of template argument deduction under 17.8.2.1 item 1.

Update: an earlier version of this answer erroneously implied that std::initializer_list<int> could be provided in place of std::initializer_list<double> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM