【问题标题】:Why is std::pair from anonymous object copying that object instead of moving?为什么来自匿名对象的 std::pair 会复制该对象而不是移动?
【发布时间】:2021-02-08 04:02:38
【问题描述】:

我尝试用这种风格制作std::pair

#include <iostream>

struct A {
    A() noexcept {
        std::cout << "Created\n";
    }
    A(const A&) noexcept {
        std::cout << "Copy\n";
    }
    A(A&&) noexcept {
        std::cout << "Move\n";
    }
};

int main() {
    std::pair<A, A> a{ {},{} };
    return 0;
}

得到这样的输出:

Created
Created
Copy
Copy

而不是

Created 
Created
Move
Move

但是如果我定义了我的匿名对象类型(例如std::pair&lt;A, A&gt; a{A{}, A{}}) 或使用std::make_pair&lt;A, A&gt;({}, {}) 我得到正确的结果。

std::pair 构造函数必须使用std::forward&lt;U1&gt;std::forward&lt;U2&gt; 来初始化对象,因此我认为我的对使用了错误的构造函数。为什么?

【问题讨论】:

  • 即使使用 C++20 它仍然调用复制 ctor
  • 您使用的是哪个编译器?哪个版本的编译器?构建时将哪些标志和选项传递给编译器?
  • 我没有使用任何优化,我在clang和msvc上编译(std::c++17都)

标签: c++ stl std-pair


【解决方案1】:

std::pair 的定义方式存在问题。我什至会说这是标准中的一个小缺陷。

它有两个可以在这里使用的构造函数:

  1. pair(const T1 &amp;x, const T2 &amp;y);,其中T1T2pair的模板参数。

  2. template &lt;class U1, class U2&gt; pair(U1 &amp;&amp;x, U2 &amp;&amp;y);

如果你做std::pair&lt;A, A&gt; a{A{}, A{}});,那么第二个构造函数被选中,一切正常。

但是如果你做std::pair&lt;A, A&gt; a{{}, {}};,编译器就不能使用第二个构造函数,因为它不能推导出U1U2,因为{}本身没有类型。所以使用第一个构造函数,你得到一个副本。

为了使其正常工作,std::pair 应该有一个额外的非模板构造函数 pair(T1 &amp;&amp;, T2 &amp;&amp;),并且最好有两个额外的构造函数:pair(const T1 &amp;, T2 &amp;&amp;)pair(T1 &amp;&amp;, const T2 &amp;)

【讨论】:

  • 让我想起 Herb Sutter 在 Twitter 上关于 CppCon 演讲的问题:twitter.com/herbsutter/status/1305292734966558721?‍♂️
  • 我宁愿它有一个不同的而不是它有的两个:template &lt;class U1 = T1, class U2 = T2&gt; pair(U1&amp;&amp; x, U2&amp;&amp; y)。不过,这种变化可能需要新的扣除指南。
猜你喜欢
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-04
  • 2013-11-07
  • 2017-02-27
  • 1970-01-01
相关资源
最近更新 更多