c++ - 防止用户从不正确的 CRTP 基础派生

标签 c++ crtp

我想不出一个合适的问题标题来描述问题。希望下面的详细信息能清楚地解释我的问题。

考虑下面的代码

#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_未初始化。第二次和第三次调用都会输出100对于data_ .

我明白为什么它会编译和运行,如果我错了,请纠正我。

当我们调用 d2.call() 时, 调用被解析为 Base<D1>::call ,这将转换 thisD1并调用D1::call_impl .因为D1确实是派生形式Base<D1> ,所以类型转换在编译时很好。

在运行时,在类型转换之后,this , 虽然它确实是 D2对象被视为 D1 ,以及调用D1::call_impl将修改应该是 D1::data_ 的内存位, 和输出。在这种情况下,这些位恰好在 D2::data_ 的位置。是。我想第二个d2.call_impl()也应该是未定义的行为,具体取决于 C++ 实现。

关键是,这段代码,虽然在本质上是错误的,但不会给用户任何错误的迹象。我在我的项目中真正做的是我有一个 CRTP 基类,它就像一个调度引擎。库中的另一个类访问 CRTP 基类的接口(interface),例如 call , 和 call将发送至call_dispatch可以是基类默认实现或派生类实现。如果用户定义派生类,这些都可以正常工作,比如 D , 确实源自 Base<D> .如果它来自 Base<Unrelated> 将引发编译时错误在哪里 Unrelated不是源自 Base<Unrelated> .但它不会阻止用户编写上述代码。

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

所以我的问题是,有什么方法可以防止用户编写错误的派生类,如上所示。也就是说,如果用户编写派生实现类,例如 D , 但他是从 Base<OtherD> 推导出来的,则应引发编译时错误。

一种解决方案是使用 dynamic_cast .但是,这很广泛,即使它有效,它也是一个运行时错误。

最佳答案

1) 将 Base 的所有构造函数设为私有(private)(如果没有构造函数,则添加一个)

2) 将 Derived 模板参数声明为 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 的任何实例。

关于c++ - 防止用户从不正确的 CRTP 基础派生,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11224838/

相关文章:

c++ - 具有 protected 派生成员的 CRTP

c++ - 非模板错误的模板定义

c++ - 衍生出奇怪的重复模板和协方差

由 C++ CRTP stack corruption

c++ - 如何在 Qt 样式表中使用嵌入字体?

c++ - 如何将真正不可变的二维数组传递给函数?

c++ - 合并两个 C++ 包

c++ - 相机抖动未激活 Unreal C++

c++ - 拔下/切换扩展显示器时是否有消息发送到应用程序窗口?

c++ - 通过 typedef template<typename T, T> 强制模板实例化 - 为什么它有效?