简体   繁体   English

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM