【问题标题】:Alignment of multiple CRTP base classes多个 CRTP 基类的对齐
【发布时间】:2020-12-07 07:11:01
【问题描述】:

在 CRTP 中,基础对象可以return a reference to the derived object via static cast

在多重继承的情况下也是如此吗?第二个基数及以后可能位于与派生对象不同的地址。例如考虑:

#include <iostream>
#include <string_view>

template<typename Derived>
struct Base1
{
    char c1;
};

template<typename Derived>
struct Base2
{
    char c2;
    auto& get2() const
    {
        return static_cast<const Derived&>(*this); // <-- OK?
    }
};

struct X : public Base1<X>, public Base2<X>
{
    X(std::string_view d) : data{d} {}

    std::string_view data;
};


int main()
{
    auto x = X{"cheesecake"};

    std::cout << x.get2().data << std::endl;
}

gcc 的未定义行为分析器says this is undefined behavior.
clang 的未定义行为分析器detects no problem.

标准是否说明了它们中的哪一个是正确的?

更新:
gcc 的 bug 已经在 trunk 上修复了。

【问题讨论】:

  • 您打算为此提交错误报告吗?
  • Done。谢谢提醒!

标签: c++ language-lawyer multiple-inheritance undefined-behavior crtp


【解决方案1】:

是的,这是定义的,你的代码没问题。多重继承是转换后的指针与原始指针不同的罕见情况。

如果你去源头:

[expr.static.cast.11]“指向 cv1 B 的指针”类型的纯右值,其中 B 是类类型,可以是 转换为“指向 cv2 D 的指针”类型的纯右值,其中 D 是 从 B 派生的完整类,如果 cv2 与 cv 限定相同, 或比 cv1 更高的 cv 资格。 ....

[expr.static.cast.2] 类型“cv1 B”的左值,其中 B 是类类型,可以转换为类型“对 cv2 D 的引用”,其中 D 是从 B 派生的类,如果 cv2 是相同的 cv-资格为或高于 cv1 的 cv 资格。 ...

这意味着您使用的强制转换是有效的,并且只要您不丢弃任何 cv 限定符,您使用指针也同样有效。

据我所知,这是 sanitizer 中的一个错误,当必须重定向引用时,该错误无法投射引用。

首先,这与 CRTP 无关。以下内容完全相同,CRTP 只是自动为我们完成。

Base2<X>* base = &x;
const X* orig = static_cast<const X*>(base);

std::cout << &x << std::endl;
std::cout << base << std::endl;
std::cout << orig << std::endl;

输出:

0x7ffc7eeab4d0
0x7ffc7eeab4d1
0x7ffc7eeab4d0

这是正确的,gcc 的消毒剂不会抱怨任何事情。

但是如果你改变指向引用的指针:

X x{"cheesecake"};

Base2<X>& base = x;
const X& orig = static_cast<const X&>(base);//Line 36

std::cout << &x << std::endl;
std::cout << &base << std::endl;
std::cout << &orig << std::endl;

你突然明白了

0x7ffdbf87cf50
0x7ffdbf87cf51
0x7ffdbf87cf50

Program stderr

example.cpp:36:14: runtime error: reference binding to misaligned address 0x7ffdbf87cf51 for type 'const struct X', which requires 8 byte alignment
0x7ffdbf87cf51: note: pointer points here
 00 00 00  60 cf 87 bf fd 7f 00 00  0a 00 00 00 00 00 00 00  66 20 40 00 00 00 00 00  6d 19 40 00 00
              ^ 

意味着输出再次正确,但 sanitizer 错误地在回滚时不会重定向引用。

【讨论】:

  • "多重继承是转换后的指针与原始指针不同的罕见情况。" 注意:在 C++ 中(通常)不能保证 ptr 到 bas 匹配一个指向偶数 w/ SI 的 ptr。实际上,在大多数情况下,ABI 都可以保证它(但如果唯一的基类是非多态的,则情况可能并非如此)。
猜你喜欢
  • 1970-01-01
  • 2014-12-04
  • 1970-01-01
  • 2021-09-10
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多