【问题标题】:CRTP and downcastingCRTP 和向下转换
【发布时间】:2021-02-09 20:48:08
【问题描述】:

previous post 中关注关于向下转换和类型安全的类似问题,我想知道以下示例是否会创建未定义的行为。

已创建 Base 类的实例。不发生动态绑定。
但是,在 Base::interface 函数中, Base 类的实例被强制转换为 Derived 类的实例。 这安全吗?如果是,为什么? 请在下面找到这段代码。

#include <iostream>
template <typename Derived>
struct Base{
  void interface(){
    static_cast<Derived*>(this)->implementation();
  }
};

struct Derived1: Base<Derived1>{
  void implementation(){
    std::cout << "Implementation Derived1" << std::endl;
  }
};
        
int main(){
  
  std::cout << std::endl;
  Base<Derived1> d1;
  d1.interface();
  std::cout << std::endl;
}

【问题讨论】:

    标签: c++ crtp


    【解决方案1】:

    这个新的Derived * 没有Derived 可以指向,所以它绝对不安全:转换本身具有未定义的行为,如[expr.static.cast]§11 中所述(强调我的):

    类型为“pointer to cv1 B”的纯右值,其中B是一个类类型,可以转换为“pointer to cv2”类型的纯右值> D”,其中 D 是从 B 派生的完整类,如果 cv2 的 cv 限定与 cv1 相同或更高时间>。 [...] 如果“指向 cv1 B 的指针”类型的纯右值指向实际上是 D 类型对象的子对象的 B,结果指针指向D 类型的封闭对象。 否则,行为未定义

    您可以通过限制对Base 的构造函数的访问来降低这种风险:

    template <typename Derived>
    struct Base{
        // Same as before...
    
    protected:
        Base() = default;
    };
    

    这样更好,但如果有人意外定义了struct Derived2 : Base&lt;AnotherDerived&gt; { };,仍然会出现同样的问题。可以通过特定的 friend 声明来防止这种情况发生,但缺点是可以完全访问 Base 的私有成员:

    template <typename Derived>
    struct Base{
        // Same as before...
    
    private:
        friend Derived;
        Base() = default;
    };
    

    请注意,这仍然让Derived 在其成员函数中构造裸Base&lt;Derived&gt; 对象,但这就是我通常停止敲打鼹鼠的地方。

    【讨论】:

    • DerivedDerived1。这就是 CRTP 背后的好奇心。你能详细说明为什么你认为它是 UB 吗?
    • @PatrickRoberts 我知道,我自己其实很喜欢这种模式。然而,OP 的情况是他们实例化了一个裸的Base&lt;Derived1&gt;不是一个Derived1
    • @PatrickRoberts,我认为那是 UB,因为在运行时 d1 的类型是 Base。但是在接口内部,它被转换为 Derived1 类的指针
    • 大多数情况下,CRTP 的构造函数应该是protected,除非它是抽象的,因此只能构造派生类型。根据我的经验,您很少能直接构造 CRTP 基类型的实例。
    • @TedLyngmo 看看这个example。在此示例中,访问了 Derived 类中的变量。使用 sanitizer 检测到内存违规
    猜你喜欢
    • 1970-01-01
    • 2017-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多