复制/移动省略与显式删除的复制/移动构造函数

Copy/move elision versus explicitly deleted copy/move constructors

本文关键字:复制 构造函数 删除 移动 移动省      更新时间:2023-10-16

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

  1. 显式delete d复制ctor或移动ctor是否可以被消除?通过跳过delete d复制ctor和/或delete d移动ctor,是否允许从另一个相同类型的对象或临时对象构造对象的尝试成功?

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

    #include <iostream>
    struct Foo {
        Foo() { std::cout << "default ctorn"; }
        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 &&)” ... cannot be referenced – it is a deleted function,编译器也没有抱怨,所以这一行仍然在编译。

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

  3. 为什么Foo{ Foo() };有效,而Foo{ f };无效?这种选择性看起来是任意的。这种右值引用相对于常量引用的任意偏好(反之亦然)似乎不适用于非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&&)不应该阻止对Foo(Foo const&)的调用;在这种情况下,CCD_ 19将具有比CCD_ 20更低的过载解决优先级。

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

    error: use of deleted function 'Foo::Foo(Foo&&)'用于Foo{ Foo() };

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

    哪个编译器是正确的?

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

[注意:必须执行此两阶段过载解决方案而不管是否会发生拷贝省略。它决定了如果未执行省略,则要调用的构造函数,并且构造函数必须是可访问的,即使调用被取消--尾注]