1

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;
};
Stack Danny
  • 7,754
  • 2
  • 26
  • 55
buttonsrtoys
  • 2,359
  • 3
  • 32
  • 52
  • 3
    I don't think `std::optional` is really designed to be inherited. I think *composition* would be a better design choice here. – Some programmer dude Jul 17 '19 at 11:23
  • check out https://stackoverflow.com/questions/31264984/c-compiler-error-c2280-attempting-to-reference-a-deleted-function-in-visual – nivpeled Jul 17 '19 at 11:23

2 Answers2

3

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).

sebrockm
  • 5,733
  • 2
  • 16
  • 39
  • Thanks! Only tweaks I needed to your "= default" solution was to add back in the constructor "constexpr OptMsg( const T& value ) : OptMsg::optional{ value } {}" so that I could "OptMsg res(false);". Also, MS gives compile error "operator =(const OptMsg &&)': is not a special member function which can be defaulted" so I rolled my own. – buttonsrtoys Jul 17 '19 at 13:45
  • See also: [rule of zero](https://en.cppreference.com/w/cpp/language/rule_of_three) – Caleth Jul 17 '19 at 13:53
  • @buttonsrtoys please see my update on your error message. No need to roll your own, it's just the wrong signature, again :) – sebrockm Jul 17 '19 at 14:04
  • @sebrockm Thanks for the const reminder. I had put in your move assignment operator and got the same error. Turns out the compiler didn't like its "OptMsg&&" return type. "OptMsg&" worked though! – buttonsrtoys Jul 17 '19 at 14:37
  • @buttonsrtoys Oh, absolutely! I copied it from you and missed to adjust the return type. It must be `OptMsg&` indeed. – sebrockm Jul 17 '19 at 14:39
1

I found some problems like:

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

I believe you mean

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

Two remarks here: Moving is not constant! The origin data will maybe modified to identify that the ownership of data was moved! You have to use std::forward to forward the rvalue ref to the base class move constructor.

Also your:

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

has to marked const!

Next thing:

constexpr OptMsg( const T& value ): OptMsg::optional{ value }{}

can not be marked as explicit because it is dedicated to do the type conversion from bool->OptMsg

Klaus
  • 24,205
  • 7
  • 58
  • 113