Home > Back-end >  Does gcc have a extension overload for std::vector::emplace_back?
Does gcc have a extension overload for std::vector::emplace_back?

Time:09-04

According to cppreference, std::vector::emplace_back has only one signature, which is:

template< class... Args >
reference emplace_back( Args&&... args );

And in the description, it says emplace_back is supposed to forward each of its arguments to the constructor of the type in the vector. However, when I tested the following code using gcc 12.2, it successfully compiles:

#include <iostream>
#include <vector>

class Foo
{
public:
    int x;
    int y;
    Foo(int x, int y) : x(x), y(y)
    {}
};

int main()
{
    std::vector<Foo> foos;
    Foo foo(1, 2);
    foos.push_back(std::move(foo));
    foos.emplace_back(foo); // weird
}

(See on compiler explorer)

I expected the line foos.emplace_back(foo); to fail the compilation. It seems as if emplace_back has this overload:

template< class T >
reference emplace_back( T& arg );

which isn't mentioned in the documentation. Am I missing something or is this just a compiler extension?

CodePudding user response:

foos.emplace_back(foo); // weird

That's weird indeed! You ask to emplace_back, meaning you're calling the constructor, and you're passing foo to the constructor. Hence, you're calling the copy constructor. push_back would have done the same!

That's an implicitly defined default copy constructor, see cppreference on Copy Constructors:

If no user-defined copy constructors are provided for a class type (struct, class, or union), the compiler will always declare a copy constructor as a non-explicit inline public member of its class.

You didn't define a copy constructor, so the compiler did that for you, as dictated by C . You can avoid that and make your compilation fail:

class Foo
{
public:
    int x;
    int y;
    Foo(int x, int y) : x(x), y(y)
    {}
    
    //! Explicitly deleted default copy constructor.
    Foo(const Foo&) = delete;
};
foos.push_back(std::move(foo));

He're you're calling the move constructor. Note that you're using foo after moving from it, that's more or less illegal (you throwing std::move at foo means that foo is no longer supposed to hold any resources.)

Should you really happen to just want to delete the move constructor, same syntax, basically: Foo(Foo&&) = delete;.

CodePudding user response:

template< class... Args >
reference emplace_back( Args&&... args );

can deduced to something like (with Args = Foo&)

reference emplace_back( Foo& && args[0] );

which collapse to

reference emplace_back( Foo& args[0] );
  • Related