I have the following code and its output printed below. I can't seem to understand why one set of braced initialization results in the move constructor being called, while the other results in the copy constructor. I have somewhat narrowed it down to direct-list-initialization vs copy-list-initialization per https://en.cppreference.com/w/cpp/language/list_initialization I just can't quite figure out which case my code belongs to. Thanks in advance.
#include <cstdint>
#include <iostream>
using namespace std;
struct Foo {
Foo() {
cout << "create foo\n";
}
~Foo() {
cout << "delete foo\n";
}
Foo(const Foo& f) {
cout << "copy foo\n";
}
Foo(Foo&& f) noexcept {
cout << "move foo\n";
}
Foo& operator=(const Foo& f) = delete;
Foo& operator=(Foo&& f) = delete;
};
int32_t main() {
pair<uint32_t, Foo> f1{0, Foo{}}; // Calls move ctor
cout << "------------------------\n";
pair<uint32_t, Foo> f2{0, {}}; // Calls copy ctor
cout << "------------------------\n";
return 0;
}
This results in
create foo
move foo
delete foo
------------------------
create foo
copy foo
delete foo
------------------------
delete foo
delete foo
Let's take a look at two of the two-argument constructors of pair
: [pairs.pair]
EXPLICIT constexpr pair(const T1& x, const T2& y);
template<class U1, class U2> EXPLICIT constexpr pair(U1&& x, U2&& y);
The second constructor uses perfect forwarding, and U2
cannot be deduced from {}
. Therefore, the first version is selected when {}
is used. When Foo{}
is used instead, the argument has type Foo
, so U2
is deduced to be Foo
, causing the forwarding version to be selected.