C++ 如何在编译时检测基类的模板参数(错误)?

C++ 如何在编译时检测基类的模板参数(错误)?,c++,templates,inheritance,derived-class,C++,Templates,Inheritance,Derived Class,我一直在使用的通用代码如下所示: template <typename T> void genericFunction(T &); template <typename T> struct Functionality { void genericMethod() { genericFunction(*((T *)this)) ; } }; struct Klass : public Functionality<Klass&g

我一直在使用的通用代码如下所示:

template <typename T> void genericFunction(T &);
template <typename T> struct Functionality {
    void genericMethod() {
        genericFunction(*((T *)this)) ;
    }
};

struct Klass : public Functionality<Klass> {};

void main() {
    Klass obj ;
    obj.genericMethod();
}

template <> void genericFunction<Klass>(Klass &obj) {
    //do stuff with Klass &obj here
}
模板无效泛型函数(T&);
模板结构功能{
void genericMethod(){
泛型函数(*(T*)this));
}
};
结构Klass:公共功能{};
void main(){
Klass obj;
obj.genericMethod();
}
模板无效通用功能(Klass和obj){
//在这里与Klass&obj合作
}
今天我遇到了一个错误,花了我大约90分钟的时间进行头发拉扯融合,这个错误是由于在基类继承声明中使用了不正确的模板参数造成的,有点像这样:

struct Klass : public Functionality<SomeOtherKlass> {}; //SomeOtherKlass wrong!!!
structKlass:公共功能{}//有人错了!!!

我想增强我的代码,以便检测派生类和基类模板参数之间的不匹配(运行时、编译时、任何时候:)),这可能吗?谢谢。

您可以使用动态转换,如果参数类型错误,它将返回null。(要使其正常工作,您至少需要一个虚拟函数,比如析构函数。)

如果您担心效率,boost有一个多态转换,它在调试模式下执行动态转换,但在生产模式下执行静态转换


(在任何情况下,最好避免使用C样式转换。)

您可以使用Boost或C++11功能在
genericMethod()
中断言关系:

BOOST_STATIC_ASSERT(( boost::is_base_of<Functionality<T>, T>::value ));
BOOST_STATIC_ASSERT((BOOST::is_base_of::value));
。。。尽管这是假设另一个类也不是从
功能派生的

另一种方法是在测试构建的运行时断言关系:

template <typename T> struct Functionality {
#ifdef TEST_BUILD
    virtual ~Functionality() {}
#endif
    void genericMethod() {
#ifdef TEST_BUILD
        assert(dynamic_cast<T*>(this));
#endif
        genericFunction(*((T *)this)) ;
    }
};
模板结构功能{
#ifdef测试生成
虚拟~Functionality(){}
#恩迪夫
void genericMethod(){
#ifdef测试生成
断言(动态_cast(this));
#恩迪夫
泛型函数(*(T*)this));
}
};

请注意,如果您向基添加了一个模板化构造函数,该构造函数接受指向任意类型的指针,那么测试将无法在内部运行

template<class U> Functionality(U *) { ... }
模板功能(U*){…}
然后,每个派生类的构造函数都可以将其this指针传递给构造函数,在构造函数体中,您只需静态断言U和T是同一类型


构造函数参数从未实际使用过,因此应完全优化。如果这是唯一的基类构造函数,你不能忘记调用它。唯一的问题是,如果您传递了其他内容。

到目前为止,最具体的建议是使用dynamic_cast在基类构造函数中公开格式错误的继承声明,如下所示:

#include <iostream>
template <typename T> struct Base {
    Base() {
        std::cout<<dynamic_cast<T *> (this)<<std::endl;
    }
    virtual void iampolymorphic(){}
};
struct Decoy {} ;
struct Pass : public Base<Pass>{}; //correct
struct Fail : public Base<Decoy>{}; //incorrect
int main() {
    Pass p ;
    Fail f ;
    return 1 ;
}
#包括
模板结构基{
Base(){

std::cout在C++11中,以下功能应该可以工作:

template<typename T> class Base
{
  friend T; // allowed in C++11
private:
  ~Base() {}
public:
  // ...
};

class Derived: public Base<Derived> {}; // OK

class WronglyDerived: public Base<Derived> {}; // Error: destructor of base class is private
模板类基类
{
friend T;//在C++11中允许
私人:
~Base(){}
公众:
// ...
};
派生类:公共基{};//确定
类错误派生:公共基类{};//错误:基类的析构函数是私有的


我开始使用此模板模式以避免虚拟函数带来的性能下降,上面示例中的genericFunction由脚本自动生成,该脚本为关联类创建序列化函数(读取和写入std::iostream)添加一个从未调用或只调用过一次的虚拟函数真的会有问题吗?你不需要将所有函数都虚拟化。有人会这么认为,但我注意到没有虚拟化的函数的性能下降,仅仅用纯虚拟函数从基类继承就足以导致性能下降,我的参与者lar上下文使用大量的树结构,因此任何轻微的影响都会被放大,unfortunately@Gearoid:让检查等仅在测试版本中编译如何?@Georg,这样的解决方案很好,但动态强制转换似乎没有暴露裂痕,我如何从功能类的上下文中使用RTTI来进行此检查k?不幸的是,它可能会这样做,取决于上下文。我认为这将是完全可靠的,如果我们能够安排功能,以确保每个T只实例化一次?这有意义吗?可能吗?@GeorgFritzsche,我现在明白了,我最终改变了我的评论。如果,对于每个功能,我们可以证明它是T的基础,那么恩,我认为我们已经做到了。真正有趣(也很困难?)的人会尝试展示功能在每个T中最多实例化一次。@Aaron:你不会想阻止同一派生类的多个实例。也许“多个实例”是错误的措辞。想象一下
struct Y:Functionality{};结构Z:功能{}
该代码在许多级别上都是错误的!但特别是,如果检测到有两个类继承自功能,那将是非常酷的。这是“双实例”问题。是否可以在不为每个派生类手动编码的情况下实现此功能?@Gearoid否。您不能自动生成它吗?不幸的是,派生类会自动生成它们self不是自动生成的,只有派生类的load/save函数是自动创建的,但测试在方法中,而不是构造函数中。在构造时,它不会工作。Brillant:)它工作了,非常感谢。如果你能详细回答这个问题,并附上一个注释解释为什么它在构造函数中不工作,我将接受它作为答案。@GearoidMurphy,你自己承认,这不起作用!(“两个动态强制转换操作的输出都是一个空指针”)。不过,根据GeorgFritzsche的评论,你非常接近。不幸的是,它在析构函数中也不起作用。我一直在尝试使用
typeid(*this).name()
@Gearoid:我把一些浓缩到我的答案中。如果支持C++11,这会更好。有一个版本在C++11之前就可以使用,但显然它是一个g++扩展。让这个子类变得有趣