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

时间:2013-12-14 23:51:13

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

我想知道copy / move elision何时应用(或允许应用)显式delete d复制/移动构造函数和非delete d复制/移动构造函数。以下是具体内容:

  1. 明确delete d复制ctor或移动ctor是否可以省略?是否试图通过跳过delete d copy ctor和/或delete d move 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 &&)” ... cannot be referenced – it is a deleted function,编译器也不会抱怨,因此该行仍在编译。

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

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

    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.
    

    根据该逻辑,当delete d Foo(Foo&&)不是临时的时,Foo(Foo const&) Foo(Foo&&)不应阻止对Foo(Foo const&)的调用;在这种情况下,Foo的重载决策优先级低于-fno-elide-constructors

  4. 我在g ++ 4.8中尝试了同样的error: use of deleted function 'Foo::Foo(Foo&&)'示例,禁用了复制省略(通过标记Foo{ Foo() };)并再次启用它。两个g ++试验都给出了:

    error: use of deleted function 'Foo::Foo(const Foo&)'

    Foo{ f }; {{1}}

    {{1}}

    哪种编译器正确?

1 个答案:

答案 0 :(得分:4)

VC ++女士有一个很老的知名bug。来自C ++标准

  

[注意:必须执行此两阶段重载决策   无论是否会发生复制。它决定了   如果未执行elision则调用的构造函数,以及所选的   即使呼叫被省略,也必须可以访问构造函数。 - 后注]