Search code examples
c++c++17subclasscopy-constructorstdoptional

Compile error while subclassing std::optional


I'm trying to subclass std::optional in MS C++17 (VS2017) to add a message field to the class, but getting the compile error

error C2280: 'OptMsg<bool>::OptMsg(const OptMsg<bool> &)': attempting to reference a deleted function

Intellisense gives a little more insight:

function "OptMsg<T>::OptMsg(const OptMsg<bool> &) throw() [with T=bool]" (declared implicitly) cannot be referenced -- it is a deleted function

Which tells me the compiler has in issue with my copy constructor referencing the deleted function throw? I get the error return an instance from a function. E.g.,

OptMsg<bool> foo()
{
    OptMsg<bool> res = false;
    return res; // <-- Getting compile error here
}

Here's my class. Any insights are appreciated!

template <class T>
class KB_MAPPING_ENGINE_API OptMsg : public std::optional<T>
{
public:
    constexpr OptMsg() noexcept
        : optional{}
        {}
    constexpr OptMsg(std::nullopt_t) noexcept
        : optional{}
        {}
    constexpr OptMsg(const T & other) noexcept
        : optional<T>{other}
        , m_Message{other.m_Message}
        {}
    constexpr explicit OptMsg(const T && other) noexcept
        : optional<T>{other}
        , m_Message{other.m_Message}
        {}
    OptMsg & operator = ( const OptMsg & other ) noexcept
    {
        if ( &other != this )
            m_Message = other.m_Message;
        return *this;
    }
    OptMsg && operator = ( const OptMsg && other )
    {
        if ( &other != this )
            m_Message = other.m_Message;
        return *this;
    }

    void SetMessage( const std::string & message ) { m_Message = message; }

    std::string GetMessage() { return m_Message; }

private:
    std::string m_Message;
};

Solution

  • Which tells me the compiler has in issue with my copy constructor referencing the deleted function throw?

    No, there is no function throw(). This notation is the pre-C++11 way of declaring that a function doesn't throw anything. Nowadays noexcept is recommended, but apparently, Microsoft didn't catch up with that yet...

    Instead, the error tells you that you are trying to call the copy constructor (in the line you marked), but your class doesn't have one!

    Why doesn't it have one? This here

    constexpr OptMsg(const T & other) noexcept
        : optional<T>{other}
        , m_Message{other.m_Message}
        {}
    

    looks like it is intended to be the copy constructor. However, it is not because the argument is const T&. A copy constructor needs const OptMsg&.

    Usually, the copy constructor would have been declared for you automatically. This did not happen here because you explicitly declared operator=(const OptMsg&). Hence, the error message is mentioning that your copy constructor is a "deleted function".

    How to fix it?

    Either declare your copy constructor correctly, or remove the assignment operator. The compiler will generate one for you. But note that your current implementations of the assignment operators (copy as well as move) only assign the message but not the optional itself. Is that intended? Would be a very unexpected behavior... If this is intended, you have to declare everything yourself (but correctly!).

    But assuming that this is not intended and further assuming that your copy operations shall just trivially copy the entire instance (message + base class), then the automatically generated constructors and assignment operators do exactly what you want, no need to write them yourself. However, you can write this into your class to make everyone see immediately that you are using the compiler generated ones:

    constexpr OptMsg() = default;
    
    constexpr OptMsg(const OptMsg & other) = default;
    constexpr OptMsg(OptMsg && other) = default;
    OptMsg & operator = ( const OptMsg & other ) = default;
    OptMsg & operator = ( OptMsg && other ) = default;
    

    Update:

    Note that move constructor and move assignment operator need OptMsg&& as argument. You keep having const OptMsg&&. Hence your error message

    "operator =(const OptMsg &&)': is not a special member function which can be defaulted

    Also have a look at the rule of zero (thanks to @Caleth).