【问题标题】:Copy/move elision versus explicitly deleted copy/move constructors复制/移动省略与显式删除的复制/移动构造函数
【发布时间】:2014-01-02 14:04:40
【问题描述】:

我想知道复制/移动省略何时适用(或允许应用)显式deleted 复制/移动构造函数和非deleted 复制/移动构造函数。具体如下:

  1. 一个明确的deleted 复制ctor 或移动ctor 可以被忽略吗?是否允许通过跳过 deleted 复制 ctor 和/或 deleted 移动 ctor 从另一个相同类型的对象或临时对象构造对象的尝试成功?

    这是 VC12 中发生的事情(我不确定是否有禁用复制/移动省略的选项):

    #include <iostream>
    
    struct Foo {
        Foo() { std::cout << "default ctor\n"; }
        Foo(Foo const&) = delete;
        Foo(Foo&&) = delete;
    };
    
    int main() {
                             // ----Output------
        Foo{ Foo() };        // "default ctor"
        Foo f;               // "default ctor"
        Foo{ std::move(f) }; // error C2280: 'Foo::Foo(Foo &&)' : attempting to reference a deleted function
        Foo{ f };            // error C2280: 'Foo::Foo(const Foo &)' : attempting to reference a deleted function
    }
    

    即使 IntelliSense 抱怨 Foo{ Foo() };: Error: function “Foo::Foo(Foo &amp;&amp;)” ... cannot be referenced – it is a deleted function,编译器也不会抱怨,因此该行仍然可以编译。

  2. 为什么Foo{ Foo() }; 有效,而Foo{ std::move(f) }; 无效?如果一个调用忽略了移动 ctor,那么另一个不应该吗?

  3. 为什么Foo{ Foo() }; 有效,而Foo{ f }; 无效?这种选择性看起来很随意。这种右值引用优于 const 引用(反之亦然)的任意偏好似乎不适用于非 ctor 方法;在调用中,如果deleted 重载具有比非deleted 重载更高的重载解决优先级,则deleted 一个会阻止非deleted 一个,从而导致编译器错误:

    struct Bar {
        void g(int const&) {}
        void g(int&&) = delete;
    };
    
    //…
    Bar b;
    b.g(2); //error C2280: 'void Bar::g(int &&)' : attempting to reference a deleted function
    // ^ Would have compiled had function `g(int&&)` been commented out.
    

    根据该逻辑,当参数不是临时参数时,deleted Foo(Foo&amp;&amp;) 不应阻止对 Foo(Foo const&amp;) 的调用;在这种情况下,Foo(Foo&amp;&amp;) 的重载解决优先级将低于Foo(Foo const&amp;)

  4. 我在 g++ 4.8 中尝试了相同的 Foo 示例,但禁用了复制省略(通过标志 -fno-elide-constructors)并再次启用它。两个 g++ 试验都给出了:

    error: use of deleted function 'Foo::Foo(Foo&amp;&amp;)' 代表Foo{ Foo() };

    error: use of deleted function 'Foo::Foo(const Foo&amp;)'Foo{ f };

    哪个编译器是正确的?

【问题讨论】:

    标签: c++ constructor move-semantics delete-operator copy-elision


    【解决方案1】:

    Ms VC++ 有一个非常古老的众所周知的错误。来自 C++ 标准

    [ 注意:必须执行此两阶段重载解决方案 不管是否会发生复制省略。它决定了 如果不执行省略,则调用构造函数,并且选择的 即使省略了调用,构造函数也必须是可访问的。 ——尾注]

    【讨论】:

    • C++ 标准的第 12.8 节复制和移动类对象的第 #31 段描述了允许实现省略类对象的复制/移动构造的标准。这是一个足够长的段落。
    猜你喜欢
    • 2023-03-03
    • 2018-09-17
    • 2019-09-29
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多