防止用户从不正确的CRTP基数派生 [英] Prevent user from deriving from incorrect CRTP base

查看:84
本文介绍了防止用户从不正确的CRTP基数派生的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想不出合适的问题标题来描述问题。希望下面的详细信息可以清楚地说明我的问题。

I cannot think about a proper question title to describe the problem. Hopefully the details below explains my problem clear.

请考虑以下代码

#include <iostream>

template <typename Derived>
class Base
{
    public :

    void call ()
    {
        static_cast<Derived *>(this)->call_impl();
    }
};

class D1 : public Base<D1>
{
    public :

    void call_impl ()
    {
        data_ = 100;
        std::cout << data_ << std::endl;
    }

    private :

    int data_;
};

class D2 : public Base<D1> // This is wrong by intension
{
    public :

    void call_impl ()
    {
        std::cout << data_ << std::endl;
    }

    private :

    int data_;
};

int main ()
{
    D2 d2;
    d2.call_impl();
    d2.call();
    d2.call_impl();
}

它将编译并运行 D2的定义是故意的错误。第一次调用 d2.call_impl()将输出一些随机位,这是因为 D2 :: data _ 未初始化。第二个和第三个调用将为 data _ 全部输出 100

It will compile and run though the definition of D2 is intentionally wrong. The first call d2.call_impl() will output some random bits which is expected as D2::data_ was not initialized. The second and third calls will all output 100 for the data_.

我了解为什么它会编译并运行,如果我错了,请纠正我。

I understand why it will compile and run, correct me if I am wrong.

调用 d2.call(),该呼叫将解析为 Base< D1> :: call ,并且将强制转换 this D1 并调用 D1 :: call_impl 。因为 D1 确实是从 Base< D1> 派生的,所以在编译时可以进行强制转换。

When we make the call d2.call(), the call is resolved to Base<D1>::call, and that will cast this to D1 and call D1::call_impl. Because D1 is indeed derived form Base<D1>, so the cast is fine at compile time.

在运行时,强制转换后,,而实际上是 D2 对象被视为 D1 ,并且对 D1 :: call_impl 的调用将修改内存应该是 D1 :: data _ 的位,然后输出。在这种情况下,这些位恰好是 D2 :: data _ 所在的位置。我认为第二个 d2.call_impl()也是根据C ++实现的未定义行为。

At run time, after the cast, this, while it is truly a D2 object is treated as if it is D1, and the call to D1::call_impl will modified the memory bits that are supposed to be D1::data_, and output. In this case, these bits happened to be where D2::data_ are. I think the second d2.call_impl() shall also be undefined behavior depending on the C++ implementation.

要点是,此代码虽然有意出错,但不会给用户带来任何错误的迹象。我在我的项目中真正要做的是,我有一个CRTP基类,其作用类似于调度引擎。库中的另一个类访问CRTP基类的接口,例如 call call ,将分派给 call_dispatch 可以是基类的默认实现或派生类的实现。如果用户定义的派生类(例如 D )确实是从 Base< D> 派生的,那么所有这些都可以正常工作。如果它是从 Base< Unrelated> 派生的,则会产生编译时错误,而 Unrelated 不是从<$ c派生的$ c> Base< Unrelated> 。但这不会阻止用户编写上述代码。

The point is, this code, while intensionally wrong, will give no sign of error to the user. What I am really doing in my project is that I have a CRTP base class which acts like a dispatch engine. Another class in the library access the CRTP base class' interface, say call, and call will dispatch to call_dispatch which can be base class default implementation or derived class implementation. These all will work fine if the user defined derived class, say D, is indeed derived from Base<D>. It will raise compile time error if it is derived from Base<Unrelated> where Unrelated is not derived from Base<Unrelated>. But it will not prevent user write code like above.

用户通过从基本CRTP类派生并提供一些实现细节来使用该库。当然,还有其他设计替代方案可以避免上述不正确使用的问题(例如抽象基类)。但是让我们暂时将它们放在一旁,只是相信我,由于某种原因我需要这种设计。

The user use the library by deriving from the base CRTP class and providing some implementation details. There are certainly other design alternatives that can avoid the problem of incorrect use as above (for example an abstract base class). But let's put them aside for now and just believe me that I need this design because of some reason.

所以我的问题是,有什么办法可以防止用户编写了不正确的派生类,如上所示。也就是说,如果用户编写一个派生的实现类,例如 D ,但他是从 Base< OtherD> 派生的,

So my question is that, is there any way that I can prevent the user from writing incorrect derived class as see above. That is, if user write an derived implementation class, say D, but he derived it from Base<OtherD>, then a compile time error shall be raised.

一种解决方案是使用 dynamic_cast 。但是,这很耗费时间,即使它起作用时也是一个运行时错误。

One solution is use dynamic_cast. However, that is expansive and even when it works it is a run-time error.

推荐答案

1)使Base的所有构造函数私有(如果没有构造函数,请添加一个)

1) make all constructors of Base private (if there are no constructors, add one)

2)将派生的模板参数声明为Base的朋友

2) declare Derived template parameter as friend of Base

template <class Derived>
class Base
{
private:

  Base(){}; // prevent undesirable inheritance making ctor private
  friend  Derived; // allow inheritance for Derived

public :

  void call ()
  {
      static_cast<Derived *>(this)->call_impl();
  }
};

在此之后,将不可能创建任何错误的继承D2实例。

After this it would be impossible to create any instances of the wrong inherited D2.

这篇关于防止用户从不正确的CRTP基数派生的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆