c++ – emplace_back для int

Question:

– Do you understand what's going on?
– Explain to you?
– I myself can explain … Do you understand or not?

Sorry for the epigraph, but … I have a feeling that I can explain, but I don’t understand (or vice versa …) what is happening here:

std::vector<int> v;
v.emplace_back(1);
v.emplace_back({1});
v.emplace_back(int{1});

How the standard interprets these three expressions, and why such a strange diagnosis

emplace_back: the function takes no 1 arguments

near the middle line?

Answer:

For this offer

v.emplace_back({1});

the problem is that when templated parameters are used, the type of the templated parameter is not inferred from the argument enclosed in curly braces. And the emplace_back function uses template parameters.

From the C ++ standard (14.8.2.1 Deducing template arguments from a function call)

1 Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std :: initializer_list for some P0 and the argument is an initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, taking P0 as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context

It is possible to simulate the same error with the following example of a class declaration

#include <iostream>
#include <initializer_list>

struct A
{
    template <typename ...T>
    void f( T &&... ) const {}
};

int main() 
{
    A().f( { 1 } );

    return 0;
}

The compiler may issue the following diagnostic message

prog.cpp: In function 'int main()':
prog.cpp:13:15: error: no matching function for call to 'A::f(<brace-enclosed initializer list>)'
  A().f( { 1 } );
               ^
prog.cpp:8:7: note: candidate: void A::f(T&& ...) const [with T = {}]
  void f( T &&... ) const {}
       ^
prog.cpp:8:7: note:   candidate expects 0 arguments, 1 provided

If you replace emplace_back with a non-templated push_back method, the corresponding call will compile, since the type of the function parameter is known from the type of the declared instantiated vector and is of type int .

Therefore, you will explicitly need to specify the type of the function template argument

For instance,

#include <iostream>
#include <initializer_list>

struct A
{
    template <typename ...T>
    void f( T &&... ) const {}
};

int main() 
{
    A().f<int>( { 1 } );

    return 0;
}

Or for emplace_back

#include <iostream>
#include <vector>
#include <initializer_list>

int main() 
{
    std::vector<int> v;

    v.emplace_back<int>( { 1 } );

    for ( int x : v ) std::cout << x << ' ';
    std::cout << std::endl;

    return 0;
}

Output of the program to the console

1

or for the example suggested by @alexolut in the comment

#include <iostream>
#include <vector>

struct S 
{
    S(std::initializer_list<int>) {}
};

int main() 
{
    std::vector<S> v;
    v.emplace_back<std::initializer_list<int>>({1});
}

As for the expression in the given call

v.emplace_back(int{1});

then an explicit type conversion is used here, the so-called functional notation.

From the C ++ standard (5.2.3 Explicit type conversion (functional notation))

3 Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a prvalue.

Scroll to Top