编译时进行C++类型检查

Mas*_*psi 1 c++ polymorphism boost typechecking assignment-operator

所有.我是C++的新手,我正在用C++编写一个小型库(主要用于我自己的项目).在设计类型层次结构的过程中,我遇到了定义赋值运算符的问题.

我采用了本文最终达到的基本方法,即对于MyClass从类派生的层次结构中的每个类,Base您定义了两个赋值运算符,如下所示:

class MyClass: public Base {
public:
    MyClass& operator =(MyClass const& rhs);
    virtual MyClass& operator =(Base const& rhs);
};

// automatically gets defined, so we make it call the virtual function below
MyClass& MyClass::operator =(MyClass const& rhs);
{
    return (*this = static_cast<Base const&>(rhs));
}

MyClass& MyClass::operator =(Base const& rhs);
{
    assert(typeid(rhs) == typeid(*this)); // assigning to different types is a logical error
    MyClass const& casted_rhs = dynamic_cast<MyClass const&>(rhs);
    try {
        // allocate new variables
        Base::operator =(rhs);
    } catch(...) {
        // delete the allocated variables
        throw;
    }
    // assign to member variables
}
Run Code Online (Sandbox Code Playgroud)

我关心的部分是类型相等的断言.由于我正在编写一个库,其中断言可能是从最终结果中编译出来的,这使我开始使用看起来更像这样的方案:

class MyClass: public Base {
public:
    operator =(MyClass const& rhs); // etc
    virtual inline MyClass& operator =(Base const& rhs)
    {
        assert(typeid(rhs) == typeid(*this));
        return this->set(static_cast<Base const&>(rhs));
    }
private:
    MyClass& set(Base const& rhs); // same basic thing
};
Run Code Online (Sandbox Code Playgroud)

但我一直想知道我是否可以在编译时检查类型.我查看了Boost.TypeTraits,我接近了BOOST_MPL_ASSERT((boost::is_same<BOOST_TYPEOF(*this), BOOST_TYPEOF(rhs)>));,但由于rhs被声明为对父类的引用而不是派生类,因此它会被阻塞.

现在我想到了,我的推理似乎很愚蠢 - 我希望由于函数是内联的,它可以自己检查实际参数,但当然预处理器总是在编译器之前运行.但我想知道是否有人知道我可以在编译时执行这种检查的任何其他方式.

CB *_*ley 7

您无法在编译时执行此断言,原因很简单,直到运行时才会知道运行时类型.

assert(typeid(rhs) == typeid(*this));
return this->set(static_cast<Base const&>(rhs));
Run Code Online (Sandbox Code Playgroud)

在你的非内联版本中dynamic_cast.如果您的断言被违反,我会保留这个以便您获得明确定义的错误而不是未定义的行为.

如果你这样做,断言要么过于严格,要么毫无意义.这dynamic_castbad_cast在调试和发布版本中引发异常.这就是你想要的.

就个人而言,我会质疑整个多态分配问题.我会遵循Scott Meyers的Effective C++建议,并在继承层次结构中抽象所有非叶子节点.然后,您可以使基类赋值运算符受保护且非虚拟.

这使您可以在派生类赋值运算符中使用它们的实现,但可以防止客户端对对象进行切片.如果客户端类只有一个基类引用或指针,那么无论如何都应该尝试分配给该类是有疑问的.如果这样做,他们应该负责铸造和类型安全保证.

  • "protected"赋值运算符的+1和非叶类的复制构造函数.无论如何,总是存在用于复制的虚拟"克隆"方法. (3认同)