Can I throw a unique_ptr?

自古美人都是妖i 提交于 2019-12-12 15:56:09

问题


I have started using C++ 11 and in particular using unique_ptr liberally to make code exception-safe and ownership more readable. This has generally worked well until I wanted to throw a unique_ptr. I have error code (thrown many places, caught in a single place) that creates complex state. Since the ownership of that dynamically allocated memory logically is being transferred from the thrower to the catcher, unique_ptr seemed like the appropriate type to indicate that and make it clear that the catcher had acquired a heap object. Didn't work, at least with the free Visual Studio 2013. Here's a boiled-down code example that no longer resembles anything useful but elicits the behavior:

// cl /nologo /EHsc /W4 test1.cpp
#include <memory>
using std::unique_ptr;
class IError
    {
public:
    virtual ~IError() {};
    virtual void DoStuff();
    };

unique_ptr<IError> Error();

int Foo() { throw Error(); }

int main(void)
    {
    try {
        Foo();
        }
    catch(unique_ptr<IError> Report)
        {
        Report->DoStuff();
        }
    return 0;
    }

And the compiler spews thusly:

test1.cpp
test1.cpp(13) : warning C4673: throwing 'std::unique_ptr<IError,std::default_delete<_Ty>>' the following types will n
ot be considered at the catch site
        with
        [
            _Ty=IError
        ]
test1.cpp(13) : warning C4670: '_Unique_ptr_base<class IError,struct std::default_delete<class IError>,1>' : this bas
e class is inaccessible
test1.cpp(13) : error C2280: 'std::unique_ptr<IError,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,
std::default_delete<_Ty>> &)' : attempting to reference a deleted function
        with
        [
            _Ty=IError
        ]
        C:\bin\Visual Studio Express 2013\VC\INCLUDE\memory(1486) : see declaration of 'std::unique_ptr<IError,std::d
efault_delete<_Ty>>::unique_ptr'
        with
        [
            _Ty=IError
        ]

Where did I go wrong?


回答1:


As a surrogate I'll be using Rextester which has MSVC version 18.00.21005.1. For GCC 4.8.1 and Clang 3.5, I'll be using Coliru. Now, initially when giving a hasty answer, I said that unique_ptrs cannot be copied and so you should be catching them by reference. However it appears the error occurs when you throw the object in MSVC. So the above advice will only apply to GCC and Clang.

catch(unique_ptr<IError>& Report)

It appears that they differ in how MSVC handles copy/move elision and/or move semantics, I'm not good enough at C++ to be more specific than that, but let's show some compilable examples. First a basic struct with a deleted copy constructor:

#include <iostream>
struct D {
    D() {};
    D(const D& other) = delete;
    D(D&& other) { std::cout << "call D move constructor... \n"; }
};

int main()
{
    try {
        throw D();
    } catch(D const& d)
    {   
    }
}

Regardless of optimization level, for both GCC and Clang, no output unless you also add -fno-elide-constructors to the invocation and we see that they both call the move constructor. For MSVC, we get this error:

source_file.cpp(22) : error C2280: 'D::D(const D &)' : attempting to reference a deleted function
        source_file.cpp(7) : see declaration of 'D::D'

For a more complicated example, see Throwing movable objects. The question is two years old yet we observe the same behavior in that GCC and Clang both call the move constructor in certain situations but MSVC calls the copy constructor in all situations (GCC and Clang differ for the Throw with object not about to die anyhow (enter non-zero integer part.)

Throw directly: 
C
caught: 007FFA7C
~
Throw with object about to die anyhow
C
c
~
caught: 007FFA74
~
Throw with object not about to die anyhow (enter non-zero integer)
C
c
caught: 007FFA70
~
1
~

TL;DR GCC and Clang will compile it but MSVC won't. A crappy workaround is to throw a pointer instead:

throw new unique_ptr<IError>;

catch(unique_ptr<IError>* Report);



回答2:


the common rule with exception is "throw by value, catch by const reference". You can't copy a unique_ptr, I guess that's part of the problem.

Derive your class Error from std::exception, and throw that.



来源:https://stackoverflow.com/questions/21715468/can-i-throw-a-unique-ptr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!