[英]Has CRTP no compile time check?
I was trying to implement static polymorphism using the Curiously Recurring Template Pattern , when I noticed that static_cast<>
, which usually checks at compile time if a type is actually convertible to another, missed a typo in the base class declaration, allowing the code to downcast the base class to one of its siblings: 我试图使用Curiously Recurring模板模式实现静态多态性,当我注意到
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;
}
Output of the program is: 该计划的产出是:
A
B
A
Why does the cast work with no errors? 为什么演员表没有错误? How can I have a compile time check that could help from this type of errors?
如何进行编译时检查可以帮助解决此类错误?
The usual way to solve this in CRTP is to make the base class have a private constructor, and declare the type in the template a friend: 在CRTP中解决此问题的常用方法是使基类具有私有构造函数,并将模板中的类型声明为朋友:
template< typename T >
struct CRTP
{
void do_it( )
{
static_cast< T& >( *this ).execute( );
}
friend T;
private:
CRTP() {};
};
In your example, when you accidentally have C
inherit from CRTP<A>
, since C
is not a friend of CRTP<A>
, it can't call its constructor, and since C
has to construct all its bases to construct itself, you can never construct a C
. 在你的例子中,当你不小心从
CRTP<A>
继承C
CRTP<A>
,由于C
不是CRTP<A>
的朋友,它不能调用它的构造函数,并且因为C
必须构造它的所有基础来构造自己,你永远不能构建一个C
The only downside is that this doesn't prevent compilation per se; 唯一的缺点是,这并不妨碍汇编本身; to get a compiler error you either have to try to actually construct a
C
, or write a user defined constructor for it. 要获得编译器错误,您必须尝试实际构造
C
,或者为其编写用户定义的构造函数。 In practice, this is still good enough and this way you don't have to add protective code in every derived as the other solution suggests (which IMHO defeats the whole purpose). 在实践中,这仍然是足够好的,这样你就不必在每个派生中添加保护代码,正如其他解决方案所暗示的那样(恕我直言打败了整个目的)。
Live example: http://coliru.stacked-crooked.com/a/38f50494a12dbb54 . 实例: http : //coliru.stacked-crooked.com/a/38f50494a12dbb54 。
NB: in my experience, the constructor for CRTP must be "user declared", which means you cannot use =default
. 注意:根据我的经验,CRTP的构造函数必须是“用户声明”,这意味着你不能使用
=default
。 Otherwise in a case like this, you can get aggregate initialization, which will not respect private
. 否则在这种情况下,您可以获得聚合初始化,这不会尊重
private
。 Again, this might be an issue if you are trying to keep the trivially_constructible
trait (which is not a terribly important trait), but usually it shouldn't matter. 同样,如果你试图保持
trivially_constructible
特征(这不是一个非常重要的特征),这可能是一个问题,但通常它无关紧要。
Q1 Why does the cast work with no errors?
Q1为什么演员表没有错误?
When none of the sensible things apply ... 当没有明智的事情适用时......
From https://timsong-cpp.github.io/cppwp/n3337/expr.static.cast#2 : 来自https://timsong-cpp.github.io/cppwp/n3337/expr.static.cast#2 :
Otherwise, the result of the cast is undefined.
否则,演员的结果是不确定的。
Q2 How can I have a compile time check that could help from this type of errors?
Q2如何进行编译时检查可以帮助解决此类错误?
I was not able to find a method that can be used in CRTP
. 我无法找到可以在
CRTP
使用的方法。 The best I could think of is to add static_assert
in the derived classes. 我能想到的最好的方法是在派生类中添加
static_assert
。
For example, if you change C
to: 例如,如果您将
C
更改为:
struct C : CRTP< A > // it should be CRTP< C >, but typo mistake
{
static_assert(std::is_base_of<CRTP<C>, C>::value, "");
void execute( )
{
cout << "C" << endl;
}
};
You will see the error at compile time. 您将在编译时看到错误。
You can simplify that to 你可以简化它
struct C : CRTP< A > // it should be CRTP< C >, but typo mistake
{
using ThisType = C;
static_assert(std::is_base_of<CRTP<ThisType>, ThisType>::value, "");
void execute( )
{
cout << "C" << endl;
}
};
Similar code needs to be added in each derived type. 需要在每个派生类型中添加类似的代码。 It's not elegant but it will work.
它不优雅,但它会起作用。
PS I wouldn't recommend using the suggested solution. PS我不建议使用建议的解决方案。 I think it's too much overhead to account for the occasional human error.
我认为考虑到偶然的人为错误是太多的开销。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.