Is it possible to return an instance of a non-movable, non-copyable type?

后端 未结 3 1712
长发绾君心
长发绾君心 2021-02-12 19:55

In VS2013 update 5, I\'ve got this:

class Lock
{
public:
    Lock(CriticalSection& cs) : cs_(cs)
    {}

    Lock(const Lock&) = delete;
    Lock(Lock&am         


        
相关标签:
3条回答
  • 2021-02-12 20:37

    In C++17, the code in Martin Bonner's answer is legal.

    The compiler is not only permitted, but also obligated to elide the copy. Live examples for Clang and GCC. C++17 6.3.2/2 (emphasis mine):

    [...] [Note: A return statement can involve an invocation of a constructor to perform a copy or move of the operand if it is not a prvalue or if its type differs from the return type of the function. A copy operation associated with a return statement may be elided or converted to a move operation if an automatic storage duration variable is returned (10.9.5). — end note]

    A prvalue here means as much as a temporary. For exact definitions and a lot of examples, see here.

    In C++11, this is indeed illegal. But it is easily remedied, by using brace initialization in the return statement, you construct the return value at the call site location, perfectly bypassing the requirement of an often-elided copy constructor. C++11 6.6.3/2:

    [...] A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (8.5.4) from the specified initializer list. [...]

    Copy-list-initialization means that only the constructor is called. No copy/move constructors are involved.

    Live examples for Clang and GCC. From Visual Studio compiler version 16.14 up, setting the correct language standard allows this code to be compiled.

    Returning non-copyable objects like this is a very powerful construct for returning e.g. std::lock_guards from functions (allowing you to easily keep a std::mutex member private) etc.

    0 讨论(0)
  • If that compiles, it is a bug in the compiler. VC2015 correctly fails to compile it.

    class Foo
    {
    public:
        Foo() {}
        Foo(const Foo&) = delete;
        Foo(Foo&&) = delete;
    };
    
    
    Foo Bar()
    {
        return Foo();
    }
    

    Gives me:

    xxx.cpp(327): error C2280: 'Foo::Foo(Foo &&)': attempting to reference a deleted function
    

    and g++ 4.9 says:

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

    The standard is very clear that a copy constructor or move constructor must exist and be accessible, even if RVO means it is not invoked.

    0 讨论(0)
  • 2021-02-12 20:56

    As of MSVC v19.14, MSVC (aka Visual Studio) also implements the C++17 behavior of allowing return of a non-copyable, non-movable object in cases where the RVO applies: https://godbolt.org/z/fgUFdf

    As rubenvb's answer indicates, this means that GCC, Clang, and MSVC all support this C++17 behavior: https://godbolt.org/z/Hq_GyG

    0 讨论(0)
提交回复
热议问题