派生自 std::exception 的类的赋值运算符

assignment operator for class derived from std::exception

提问人:metablaster 提问时间:10/19/2019 最后编辑:metablaster 更新时间:10/20/2019 访问量:202

问:

我从中派生了一个自定义异常类std::runtime_error

静态分析器给我一个警告,如果我定义或删除默认操作(复制 ctors、复制/移动运算符、析构函数等),我应该定义或删除它们。

为了解决这个愚蠢的警告,我编写了缺少的赋值运算符,但后来我收到了另一个警告,现在我的运算符隐藏了基本的非虚拟赋值运算符!

由于基类具有我无法复制的私有成员,因此看起来唯一的解决方案是直接为基本对象部分调用基本委托运算符,然后复制对象的其余部分并最终返回*this*this

但在这样做之前,我看了一下底座的作用以及它的样子:operator=

exception& operator=(exception const& _Other) noexcept
{
    if (this == &_Other)
    {
        return *this;
    }

    __std_exception_destroy(&_Data);
    __std_exception_copy(&_Other._Data, &_Data);
    return *this;
}
private:

    __std_exception_data _Data;
};

现在知道这是我的实现(带有注释)来调用基本委托并复制派生对象的其余部分:

class Exception :
    public std::runtime_error
{
public:

    // ...

    Exception& operator=(const Exception& other)
    {
        if (this == &other)
        {
            return *this;
        }

        // first copy only base class data to *this
        *dynamic_cast<std::runtime_error*>(this) =
            runtime_error::operator=(
                *dynamic_cast<std::runtime_error*>(
                    const_cast<Exception*>(&other)));

        // then copy derived class data to *this
        mInfo = other.mInfo;
        mCode = other.mCode;

        // finally return complete copy
        return *this;
    }

private:
    std::error_code mCode;
    std::string mInfo;
};

这是正确的方法吗?我认为这看起来很麻烦,但我不确定。

编辑

以下是完整的课程,供参考:

#pragma warning (disable : 4275)    // base needs to have DLL interface
    class ERROR_API Exception :
        public std::runtime_error
    {
    public:
        ~Exception() noexcept;  // cant be inlined in release build

        // default/delete
        Exception(const Exception&) = default;
        Exception(Exception&&) = delete;

        Exception& operator=(const Exception& other)
        {
            if (this == &other)
            {
                return *this;
            }

            // copy base class data to *this
            *dynamic_cast<std::runtime_error*>(this) =
                runtime_error::operator=(
                    *dynamic_cast<std::runtime_error*>(
                        const_cast<Exception*>(&other)));

            // copy derived class data to *this
            mInfo = other.mInfo;
            mCode = other.mCode;

            return *this;
        }

        Exception& operator=(Exception&&) = delete;

        /** Construct from error enum */
        template<typename Enum>
        Exception(Enum err_enum);

        /** Construct from error enum and string*/
        template<typename Enum>
        Exception(Enum err_enum, String message);

        /** Construct from error_code object */
        inline Exception(std::error_code err_code);

        /** Construct from error_code object and string */
        inline Exception(std::error_code err_code, String message);

        /** Get error_condidtion name */
        inline virtual std::string ConditionName() const;

        /** Get error_category name */
        inline virtual std::string CategoryName() const;

        /** Get error_condition value */
        inline virtual int ConditionValue() const noexcept;

        /** Get error_condition value */
        inline virtual int ErrorValue() const noexcept;

        /** Get additional information string passed to constructor */
        inline virtual const String& GetInfo() const noexcept;

        /** Get error_code object associated with this exception object */
        inline virtual const std::error_code& code() const noexcept;

    private:
        SUPPRESS(4251);     // member needs to have DLL interface
        std::error_code mCode;
        SUPPRESS(4251);     // member needs to have DLL interface
        String mInfo;
    };
#pragma warning (default : 4275)    // base needs to have DLL interface
C++ 继承 赋值运算符

评论

1赞 aschepler 10/19/2019
那么,为什么需要实现或删除任何特殊成员呢?这看起来像一个很好的零法则类。
0赞 John Zwinck 10/19/2019
在你开始向它添加东西之前,请发布原始类,只是基础知识,也许它没问题,你的静态分析器(哪个?)过于激进。
2赞 Ulrich Eckhardt 10/19/2019
首先,静态分析器为您提供了两种可能的方法来解决这个问题。我认为你选错了,删除这些将是我的选择。也就是说,所有这些dynamic_cast的东西都不是必需的,只要记住它也可以作为方法调用,所以你只需调用调度到基类即可。operator=runtime_error::operator=(other)
1赞 Peter 10/19/2019
如果您打算实现其他默认操作,请让编译器定义它们,直到您准备好定义自己的操作。最多将它们定义为稍后可以完成的存根。无论如何,隐藏警告可能是因为您的警告与继承的警告不一致。s 具有规范(在 C++11 之前)或(C++ 11 及更高版本)。派生类需要与此保持一致 - 否则,根据标准,您将隐藏基类版本。operator=()std::exceptionoperator=()throw()noexceptoperator=()
1赞 Peter 10/19/2019
鉴于静态分析器与编译器及其库(定义派生类的位置)不同,您可能对此无能为力。只需记录静态分析仪发出警告的事实,并且您已经确定它是可以接受的(即误报)。如果您的静态分析器支持此类内容,请向类添加注释,以防止静态分析器发出警告。您需要阅读静态分析仪的文档,以确定这是否可行,如果可行,如何可行。std::exceptionException

答:

0赞 metablaster 10/19/2019 #1

感谢 Ulrich Eckhardt、Peter 和其他人的精彩评论,以下是我如何让它工作,结果根本没有警告:

class Exception : public std::runtime_error
{
public:
    ~Exception() noexcept;  // can't be inlined in release build (defaulted in source)

    // default/delete
    Exception(const Exception&) noexcept = default;
    Exception(Exception&&) noexcept = default;
    Exception& operator=(const Exception&) noexcept = default;
    Exception& operator=(Exception&&) noexcept(false) = deault;

    // the rest of the code ...
};

评论

0赞 Eljay 10/19/2019
在一个我无法删除 move-assignment 但该类不支持(也不能)支持 move 语义的类中,我只是做了一个直通来抑制静态分析器。我还发表了一条评论,解释了为什么会做坏事,因为这个可怜的灵魂必须维护该代码。Exception& operator=(Exception&& that) noexcept { *this = that; return *this; }