有没有CRTP没有编译时间检查?

nya*_*108 9 c++ static-cast static-polymorphism class-template

我试图使用奇怪的重复模板模式实现静态多态性,当我注意到static_cast<>,通常在编译时检查一个类型是否实际可以转换为另一个,在基类声明中错过了一个错误,允许代码向下转换基础与其中一个兄弟姐妹同课:

#include <iostream>

using namespace std;

template< typename T >
struct CRTP
{
    void do_it( )
    {
        static_cast< T& >( *this ).execute( );
    }
};

struct A : CRTP< A >
{
    void execute( )
    {
        cout << "A" << endl;
    }
};

struct B : CRTP< B >
{
    void execute( )
    {
        cout << "B" << endl;

    }
};

struct C : CRTP< A > // it should be CRTP< C >, but typo mistake
{
    void execute( )
    {
        cout << "C" << endl;
    }
};

int main( )
{
    A a;
    a.do_it( );
    B b;
    b.do_it( );
    C c;
    c.do_it( );
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

该计划的产出是:

A
B
A
Run Code Online (Sandbox Code Playgroud)

为什么演员表没有错误?如何进行编译时检查可以帮助解决此类错误?

Nir*_*man 8

在CRTP中解决此问题的常用方法是使基类具有私有构造函数,并将模板中的类型声明为朋友:

template< typename T >
struct CRTP
{
    void do_it( )
    {
        static_cast< T& >( *this ).execute( );
    }
    friend T;
private:
    CRTP() {};
};
Run Code Online (Sandbox Code Playgroud)

在你的例子中,当你不小心C继承CRTP<A>,因为C它不是它的朋友CRTP<A>,它不能调用它的构造函数,并且由于C必须构造它的所有基础来构造它自己,你永远不能构造一个C.唯一的缺点是,这并不妨碍汇编本身; 要获得编译器错误,您必须尝试实际构造一个C或为其编写用户定义的构造函数.在实践中,这仍然是足够好的,这样你就不必在每个派生中添加保护代码,正如其他解决方案所暗示的那样(恕我直言打败了整个目的).

实例:http://coliru.stacked-crooked.com/a/38f50494a12dbb54.

注意:根据我的经验,CRTP的构造函数必须是"用户声明",这意味着你不能使用=default.否则在这种情况下,您可以获得聚合初始化,这将不会受到尊重private.同样,如果你试图保持trivially_constructible特性(这不是一个非常重要的特征),这可能是一个问题,但通常它应该无关紧要.