29
vector<vector<int>> res;
res.emplace_back({1,2}); // change to res.push_back({1,2}); would work

This gives me the error:

main.cpp:61:25: error: no matching function for call to ‘std::vector<std::vector<int> >::emplace_back(<brace-enclosed initializer list>)’
main.cpp:61:25: note: candidate is:
In file included from /usr/include/c++/4.7/vector:70:0,
                 from /usr/include/c++/4.7/bits/random.h:34,
                 from /usr/include/c++/4.7/random:50,
                 from /usr/include/c++/4.7/bits/stl_algo.h:67,
                 from /usr/include/c++/4.7/algorithm:63,
                 from miscalgoc.hpp:1,
                 from main.cpp:1:
/usr/include/c++/4.7/bits/vector.tcc:92:7: note: void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {}; _Tp = std::vector<int>; _Alloc = std::allocator<std::vector<int> >]

How do I make this work? Also, why is an allocator needed here?

1
  • what is your practical problem with using push_back instead of emplace_back? What is the name and version of your compiler and standard library you are using? Commented Dec 5, 2013 at 5:22

5 Answers 5

51

The problem is that function template arguments doesn't deduce std::initializer_list from a braced-init-list (like { 1, 2 }).

Example:

#include <initializer_list>
#include <type_traits>


template<typename T>
void func(T arg) {
}


int main() {
    auto init_list = {1, 2};    // This works because of a special rule
    static_assert(std::is_same<decltype(init_list), std::initializer_list<int>>::value, "not same");

    func(std::initializer_list<int>{1, 2});     // Ok. Has explicit type.

    func({1, 2});   // This doesn't because there's no rule for function
                    //      template argument to deduce std::initializer_list
                    //      in this form.
}

Live example

std::vector::emplace_back() is a function template with its arguments being deduced. So passing it {1, 2} will not work because it couldn't deduce it. Putting an explicit type to it

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

would make it work.

Live example

6

@Mark's answer is pretty correct. Now let's consider a more practical case. After some proper operations, you've collected some data with vector<int>, and feel like pushing it into vector<vector<int>>:

std::vector<std::vector<int>> res;

for (int i = 0; i < 10000; ++i) {
    //
    // do something
    //
    std::vector<int> v(10000, 0);  // data acquired
    res.push_back(v);
}

It's not like assigning values you already know. Utilizing std::initializer_list is probably no longer a solution. In such cases, you may use std::move (along with either emplace_back or push_back is acceptable)

for (int i = 0; i < 10000; ++i) {
    std::vector<int> v(10000, 0);  // will become empty afterward
    res.emplace_back(std::move(v));  // can be replaced by 
                                     // res.push_back(std::move(v));
}

The performance is more or less improved. You can still be benefited from the concept of xvalue move-insertion, constructing objects by move-constructor rather than copying.

UPDATE

The reason that res.push_back(move(v)) works is because they overload the method std::vector::push_back(value_type&& val) after C++11. It is made to support rvalue reference deliberately.

4

Take a look at the documentation for vector::emplace_back. emplace_back tries to create a new element in your vector, by calling the constructor for the new element with the arguments passed in. So basially, when you call emplace_back({1,2}), it tries to pass {1,2} in to a constructor, but since res is a vector of vectors of ints, it's looking at vector constructors, none of which can take a brace-enclosed initializer list.

Also, take a look at the documentation for vector::push_back. When push_back is called, it creates a default object (in this case, a vector of ints) and copies the values into it. I would guess that the reason that push_back({1,2}) works is that the brace-enclosed initializer list creates a value-type, which push_back accepts.

1
  • push_back doesn't have this problem though.
    – user40129
    Commented Dec 5, 2013 at 4:56
1

Though the question is well answered at the moment, I would like to elaborate on exactly why push_back works in this case.

From this cppreference page we see that

std::initializer_list object is automatically constructed when:

  1. a braced-init-list is used to list-initialize an object, where the corresponding constructor accepts an std::initializer_list parameter,
  2. a braced-init-list is used as the right operand of assignment or as a function call argument, and the corresponding assignment operator/function accepts an std::initializer_list parameter,
  3. a braced-init-list is bound to auto, including in a ranged for loop.
  1. is not our case.

bush_back per se does not accept initializer_list, but it accepts T. T in our case is std::vector<int>. So 2. also does not fit.

This list of ways to list initialize mentions this one, among others:

function ({ arg1, arg2, ... })

So, putting everything together, you call push_back that accepts std::vector<int> with braced-init-list. std::vector can be list-initialized with it, so the vector is constructed and passed to push_back (well, reference to it).

emplace_back does not work with braced-init-list as it is not accepting arguments of type T (std::vector<int> in our case).

You can try something like this:

#include <iostream>
#include <initializer_list>

struct V {
    int i;
    int j;

    V(int i, int j) {
        std::cout << "i j constructor\n";
    }

    V(std::initializer_list<int> il) {
        std::cout << "init list constructor\n";
    }
};

void
test_f(const V &v) {
    std::cout << "test_f called\n";
}

int main(void) {
    test_f( {1, 2, 3} );
    return 0;
}

The output would be:

init list constructor
test_f called
-1

vector<vector > res;

res.emplace_back({1,2});

Default type detection and conversion happens only once. It does not work twice in it's use cases. For this to work you need two deductions.

  1. {1,2} -> deduce that the container is an initializer_list of Integers.

  2. res.emplace_back( any_initializer_list_of_ints ) -> here since the member element type is known to be vector of integers AND the initializer list of integers can be used to construct a vector of integers, so the initializer_list would need to be converted to vector.

The compilers don't deduce and convert it twice at the same spot. So this will never work.

Not the answer you're looking for? Browse other questions tagged or ask your own question.