在 std::pair 支撑初始化中复制 vs 移动

Copy vs move in std::pair braced initialization

我有以下代码及其输出打印如下。我似乎无法理解为什么一组大括号初始化导致调用移动构造函数,而另一组导致调用复制构造函数。我已经将其缩小到直接列表初始化与复制列表初始化 https://en.cppreference.com/w/cpp/language/list_initialization 我只是不太清楚我的代码属于哪种情况。提前致谢。

#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;
}

这导致

create foo

move foo

delete foo

------------------------

create foo

copy foo

delete foo

------------------------

delete foo

delete foo

我们来看看pair的两个双参数构造函数:[pairs.pair]

EXPLICIT constexpr pair(const T1& x, const T2& y);
template<class U1, class U2> EXPLICIT constexpr pair(U1&& x, U2&& y);

第二个构造函数使用完美转发,无法从{}推导出U2。因此,使用{}时选择第一个版本。当使用 Foo{} 时,参数的类型为 Foo,因此 U2 被推断为 Foo,导致选择转发版本。