2010-04-12 77 views
1

全部。我对C++相当陌生,并且正在用C++编写一个小型库(主要用于我自己的项目)。在设计类型层次结构的过程中,我遇到了定义赋值运算符的问题。编译时的C++类型检查

我所采取的最终在this article达到的基本方法,这是一个为每个类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 
} 

的一部分我关心的是类型平等的断言。由于我正在写一个图书馆,断言大概会被编出来的最终结果,这导致我去同一个方案,它看起来更像是这样的:

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

但我一直在想,如果我可以在编译时检查类型。我看着Boost.TypeTraits,并且通过做BOOST_MPL_ASSERT((boost::is_same<BOOST_TYPEOF(*this), BOOST_TYPEOF(rhs)>));来接近,但是由于rhs被声明为对父类而不是派生类的引用,所以它被阻塞了。

现在我想到了,我的推理看起来很愚蠢 - 我希望既然函数是内联的,它可以自己检查实际参数,但是当然预处理器总是在编译器之前运行。但是我想知道是否有人知道我可以在编译时执行这种检查。

+1

没有办法在编译时检查类型,因为这是多态性的要点:事物的类型只在运行时确定。 '动物*动物=兰特()%2?新的狗():新的猫();' – UncleBens 2010-04-12 07:02:42

+2

好文章链接的方式,谢谢:) – 2010-04-12 09:14:38

回答

7

你不能在编译时执行这个断言,原因很简单,因为运行时类型直到运行时才会知道。

assert(typeid(rhs) == typeid(*this)); 
return this->set(static_cast<Base const&>(rhs)); 

在非内联版本中,您有dynamic_cast。我会保留这一点,以便在违反断言的情况下获得定义明确的错误而不是未定义的行为。

如果你这样做,断言要么过于严格,要么毫无意义。 dynamic_cast将在调试版本和发布版本中引发bad_cast异常。这是你想要的。个人而言,我会质疑整个多态分配问题。我会遵循Scott Meyers的Effective C++建议,并在继承层次结构摘要中创建所有非叶节点。然后,您可以使基类赋值运算符受保护并且不是虚拟的。

这使您可以在派生类的赋值运算符中使用它们的实现,但会阻止客户端切片对象。如果一个客户端类只有一个基类引用或指针,那么是否应该尝试分配给该类仍然值得怀疑。如果这样做,他们应该负责铸造和类型安全保证。

+3

+ 1为'保护的'赋值运算符和非叶类的复制构造函数。无论如何总是有复制的虚拟克隆方法。 – 2010-04-12 09:11:45

0

如果你想在编译时确定事物,那么动态多态(虚拟函数等)不适合你。我一般认为没有必要将动态多态与“普通值类型”混合在一起 - 比如赋值运算符。也许具体的和非多态的类是最适合你的情况。但很难说,因为你没有谈论你想要做的事情。