Copy/move elision versus explicitly deleted copy/move constructors

839 views Asked by At

I want to know when copy/move elision applies (or is allowed to apply) to explicitly deleted copy/move constructors and to non-deleted copy/move constructors. Here are the specifics:

  1. Can an explicitly deleted copy ctor or move ctor get elided? Is an attempt to construct an object from another same-type object or temporary object ever allowed to succeed by skipping over the deleted copy ctor and/or deleted move ctor?

    Here’s what happens in VC12 (with which I’m not sure if there is an option to disable copy/move elision):

    #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
    }
    

    Even though IntelliSense complains about Foo{ Foo() };: Error: function “Foo::Foo(Foo &&)” ... cannot be referenced – it is a deleted function, the compiler doesn't complain there, so that line still compiles.

  2. Why does Foo{ Foo() }; work, but not Foo{ std::move(f) };? If one call elides the move ctor, then shouldn’t the other?

  3. Why does Foo{ Foo() }; work, but not Foo{ f };? This selectivity looks arbitrary. This kind of arbitrary preference of rvalue reference over const reference (or vice versa) doesn’t seem to apply to non-ctor methods; where in a call, if a deleted overload that would otherwise have a higher overload resolution priority than a non-deleted overload, the deleted one blocks the non-deleted one, resulting in a compiler error:

    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.
    

    According to that logic, a deleted Foo(Foo&&) shouldn’t block a call to Foo(Foo const&) when the argument is not a temporary; Foo(Foo&&) would have a lower overload resolution priority than Foo(Foo const&) in that case.

  4. I tried the same Foo example in g++ 4.8 with copy elision disabled (via the flag -fno-elide-constructors) and again with it enabled. Both g++ trials gave:

    error: use of deleted function 'Foo::Foo(Foo&&)' for Foo{ Foo() }; and

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

    Which compiler is correct?

1

There are 1 answers

1
Vlad from Moscow On BEST ANSWER

Ms VC ++ has a very old well-known bug. From the C++ Standard

[ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]