std::pair的花括号初始化中,复制和移动有什么区别?

3
我有以下代码以及下面的输出。我似乎无法理解为什么一组花括号初始化会导致调用移动构造函数,而另一组则导致调用复制构造函数。我已经将其缩小到直接列表初始化与复制列表初始化之间,详见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
1个回答

4
让我们来看一下 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,因此推断出 U2Foo,导致选择转发版本。


虽然你的解释关于编译器选择调用哪个版本是有道理的,但我似乎无法说服自己这确实是情况。我将Foo构造函数指定为“explicit”,然后出现了以下错误: error: converting to 'const Foo' from initializer list would use explicit constructor 'Foo::Foo()' - tree
@tree 你是否在使用C++17?在C++17中,这两个构造函数都是有条件的explicit,即如果它们需要调用的任何构造函数是explicit,则它们也是explicit。它们在之前的版本中是无条件的explicit,而改变为有条件的explicit是作为一个缺陷报告来实现的。 - L. F.
是的,我正在使用c++17。 所以你的意思是因为我将Foo构造函数声明为explicit,我必须使用Foo{}而不是{} - tree
@tree 是的。因此,将 Foo{} 传递给参数也可以正常工作。 - L. F.
让我试着总结一下我所学到的内容... 当使用{}初始化Foo时,编译器无法确定完美转发std::pair构造函数的类型,因为类型缺失,因此下一个最佳匹配是std::pair复制构造函数,其中Foo参数是const T2& y,这导致调用了Foo的复制构造函数。通过在Foo的构造函数中添加explicit,编译器拒绝了在匹配const T2& y时隐式构造Foo - tree
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接