1

我正在尝试在 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;
};
4

2 回答 2

3

这告诉我编译器在引用已删除函数 throw 的复制构造函数方面存在问题?

不,没有功能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 &&)': 不是可以默认的特殊成员函数

还可以查看零规则(感谢@Caleth)。

于 2019-07-17T12:12:36.977 回答
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将右值引用转发到基类移动构造函数。

还有你的:

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

必须标记const

下一件事:

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

不能标记为,explicit因为它专门用于从 bool->OptMsg 进行类型转换

于 2019-07-17T11:42:13.460 回答