0

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?

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
palapapa
  • 573
  • 2
  • 5
  • 25
  • 1
    `T&&` as forwarding reference can deduced to `T&` – apple apple Sep 03 '22 at 11:18
  • 1
    A **template** `T&&` parameter is a *forwarding reference*, and it is **not** quite the same as a non-template `Foo&&` parameter *rvalue reference* (a "moving" reference). – Eljay Sep 03 '22 at 11:40

2 Answers2

1
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;.

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
1
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] );
apple apple
  • 10,292
  • 2
  • 16
  • 36