【问题标题】:Copy vs move in std::pair braced initialization复制与移动 std::pair 支撑初始化
【发布时间】:2020-07-23 20:42:31
【问题描述】:

我有以下代码及其输出打印在下面。我似乎无法理解为什么一组大括号初始化导致调用移动构造函数,而另一组导致调用复制构造函数。我根据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

【问题讨论】:

    标签: c++ templates initialization move-semantics std-pair


    【解决方案1】:

    我们来看看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,导致选择转发版本。

    【讨论】:

    • 虽然您对编译器选择调用哪个版本的解释是有道理的,但我似乎无法说服自己确实如此。我将 Foo 构造函数指定为“显式”,我收到一个错误,内容为:错误:从初始化列表转换为“const Foo”将使用显式构造函数“Foo::Foo()”
    • @tree 你在使用 C++17 吗?在 C++17 中,这两个构造函数都是有条件的 explicit,即,如果它们需要调用的任何构造函数是 explicit,则为 explicit。在以前的版本中,它们是无条件的explicit,对有条件的explicit 的更改被应用为缺陷报告。
    • 是的,我使用的是 c++17。所以你说的是因为我明确声明了我的 Foo 构造函数,我必须使用Foo{} 而不是{}
    • @tree 哦,你的意思是编译器拒绝了{}?是的,在这种情况下explicit 构造函数不被考虑,因为参数传递是复制初始化(即非explicit 初始化)。
    • 是的,当我将 explicit 添加到 Foo 构造函数时,编译器拒绝了 {} 初始化 Foo
    猜你喜欢
    • 2018-01-13
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多