【问题标题】:CRTP with reinterpret_cast into target class带有 reinterpret_cast 的 CRTP 到目标类
【发布时间】:2018-04-30 20:06:36
【问题描述】:

背景

我希望在临时基础上应用外观,而不是将它们烘焙到类本身中。但是我需要对数据进行操作,所以我需要this 可以从门面访问。这是一个小例子:

#include <array>
#include <iostream>

template <typename T>
struct x_getter
{
    friend T;

    double x() const
    {
        return (*real_self)[0];
    }

    void x(double new_x)
    {
        (*real_self)[0] = new_x;
    }

private:
    T* real_self = reinterpret_cast<T*>(this);
    x_getter() = default; //prevents accidental creation
};

struct coordinates : std::array<double, 3>, x_getter<coordinates>
{
    using std::array<double, 3>::array;
};


int main()
{
    coordinates origin{};
    std::cout << origin.x();
    origin.x(12.7);
    std::cout << ' ' << origin.x() << '\n';
}

It segfaults。不久前使用类似的东西,我很不幸能够逃脱它。

问题

如何使目标类类型的this 在外观类中可用?

我对班级布局的理解

在对象内部的某处,以无序的方式,有数组和x_getter。通过reinterpret_casting 它,我试图欺骗它认为thiscoordinates,但是当它执行operator[] 时,使用的偏移量有点偏离,它超出了对象,因此出现段错误。

【问题讨论】:

  • 要获得派生类,您应该在方法内部使用static_cast
  • @VTT,您能否发布一个包含方法的答案,并且可能是一些链接供我阅读。谢谢,它有效。

标签: c++ language-lawyer c++17 crtp


【解决方案1】:

这里的问题是reinterpret_cast 不起作用,因为this 指针没有指向coordinates 类的开头,因为它在从x_getter 继承之前继承自array。这个类的内存布局如下所示:

coordinates
|- std::array<double, 3>
|- x_getter

当您使用reinterpret_cast&lt;T*&gt;(this) 时,存储在this 指针中的地址是x_getter 对象的地址,但是您强制编译器假定它实际上是coordinates 对象的地址。所以解引用这样一个指向派生类的指针会导致各种未定义的行为。

通常 CRTP 应该在方法内部使用static_cast

double x() const
{
    return (*static_cast<TDerived const *>(this))[0];
}

reinterpret_cast 不同,static_cast 将正确调整this 指针以正确指向派生对象。

【讨论】:

  • 哦,所以继承顺序影响布局?我认为布局相对于继承顺序是无序的。
  • @Incomputable 在此处使用reinterpret_cast 仍然是错误的,即使没有继承array 类。
  • 我相信我明白了为什么它是非常错误的。这就像没有将基指针移到正确的位置,而是将其留在原处。谢谢,现在我的理解没有漏洞了。
  • @Incomputable 正确移动指针的是static_castreinterpret_cast&lt;T*&gt; 是一个直接命令,假设参数中存储的任何内容已经是指向 T 的有效指针。
  • 那么 std::launder 是一个安全的 reinterpret_cast 吗?
猜你喜欢
  • 2019-08-04
  • 2020-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 1970-01-01
  • 2016-04-16
  • 1970-01-01
相关资源
最近更新 更多