子类化std :: optional时编译错误

时间:2019-07-17 11:18:06

标签: c++ c++17 subclass copy-constructor stdoptional

我正在尝试在MS C ++ 17(VS2017)中将std :: optional子类化,以向该类添加消息字段,但出现编译错误

  

错误C2280:'OptMsg<bool>::OptMsg(const OptMsg<bool> &)':尝试引用已删除的函数

Intellisense提供了更多的见识:

  

函数“ OptMsg<T>::OptMsg(const OptMsg<bool> &) throw() [with T=bool]”(隐式声明)无法引用-它是已删除的函数

哪个告诉我编译器在引用删除的函数throw的复制构造函数中有问题?我收到从函数返回实例的错误。例如,

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

这是我的课。感谢您提供任何见解!

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

2 个答案:

答案 0 :(得分:3)

  

哪个告诉我编译器与我的复制构造函数有关   引用已删除的函数引发?

否,没有功能throw()。这种表示法是C ++ 11之前的方法,用于声明函数不会抛出任何东西。现在推荐使用noexcept,但是显然,微软还没有赶上...

相反,该错误告诉您您正在尝试调用复制构造函数(在标记的行中),但是您的类没有一个!

为什么没有一个?这里

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

看起来像是要用作复制构造函数。但是,这不是因为参数是const T&。复制构造函数需要const OptMsg&

通常,将自动为您声明复制构造函数。由于您显式声明了operator=(const OptMsg&),因此这里没有发生。因此,错误消息提到您的副本构造函数是“已删除的函数”。

如何解决?

要么正确声明您的副本构造函数,要么删除赋值运算符。编译器将为您生成一个。 但是请注意,您当前的分配运算符(复制和移动)实现仅分配消息,而不分配optional本身。那是故意的吗?将会是一种非常意外的行为...如果要这样做,则必须自己声明所有内容(但必须正确!)。

但是假设这不是故意的,并且进一步假设您的复制操作应仅复制整个实例(消息+基类),那么自动生成的构造函数和赋值运算符将完全按照您的意愿进行操作,而无需编写它们你自己但是,您可以将其写入类中,以使每个人立即看到您正在使用编译器生成的代码:

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;

更新:

请注意,移动构造函数和移动赋值运算符需要OptMsg&&作为参数。您一直拥有const OptMsg&&。因此,您的错误消息

  

“ operator =(const OptMsg &&)':不是可以默认的特殊成员函数

还要看看the rule of zero(感谢@Caleth)。

答案 1 :(得分:1)

我发现了一些问题,例如:

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

我相信你的意思

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

这里有两句话: 移动不是恒定的!原始数据可能会被修改以标识数据的所有权已移动! 您必须使用std::forward将右值ref转发到基类move构造函数。

也是您的

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

必须标记为const

下一件事:

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

不能标记为explicit,因为它专用于从bool-> OptMsg

进行类型转换