用C++重新解释这个:合法与否?

MrM*_*ter 9 c++ language-lawyer reinterpret-cast c++17

这是一个有点深奥的问题,但我很好奇以下类扩展模式在现代C++中是否合法(如,不构成UB)(出于所有目的和目的,我将讨论局限于C++ 17和后来).

template<typename T>
struct AddOne {
    T add_one() const {
        T const& tref = *reinterpret_cast<T const*>(this);
        return tref + 1;
    }
};

template<template<typename> typename  E, typename T>
E<T> const& as(T const& obj) {
    return reinterpret_cast<E<T> const&>(obj);
} 

auto test(float x) {
    return as<AddOne>(x).add_one();
}

auto test1(int x) {
    return as<AddOne>(x).add_one();
}

// a main() to make this an MVCE
// will return with the exit code 16
int main(int argc, const char * argv[]) {
  return test1(15);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码是一个完整的例子,它在C++ 17模式下编译,运行并产生至少clang的预期结果.检查编译器资源管理器上的反汇编代码:https://godbolt.org/z/S3ZX2Y

我的解释如下:标准状态reinterpret_cast可以在任何类型的指针/引用之间进行转换,但访问结果引用可能是UB(根据别名规则).同时,将结果值转换回原始类型可保证产生原始值.

基于此,仅重新引用float作为引用的引用AddOne<float>不会调用UB.由于我们从不尝试访问该引用后面的任何内存作为实例AddOne<float>,因此这里也没有UB.我们只使用该引用的类型信息来选择add_one()成员函数的正确实现.函数本身将引用转换回原始类型,因此再次没有UB.从本质上讲,这种模式在语义上等同于:

template<typename T>
struct AddOne {
   static T add_one(T const& x) {
      return x + 1;
   }
};

auto test(float x) {
  return AddOne<Int>::add_one(x);
}
Run Code Online (Sandbox Code Playgroud)

我是正确的还是我想念的东西?

将此视为探索C++标准的学术练习.

编辑:这不是什么时候重复使用reinterpret_cast?因为该问题不讨论转换this指针或使用reinterpret_cast重新解释类型的分派.

Sne*_*tel 10

不,那绝对不合法。由于多种原因。

第一个原因是,您已经*this取消引用了AddOne<int>*实际上并不指向的AddOne<int>。没关系,该操作实际上不需要在“幕后”取消引用;*foo仅当foo指向兼容类型的对象时才合法。

第二个原因是相似的:您在非上调用成员函数AddOne<int>。同样,不访问任何一个AddOne(不存在的)成员也没关系:函数调用本身是对对象值的访问,并且违反了严格的别名规则。


MrM*_*ter 0

完整的答案是由用户@nm在评论中提供的,因此为了完整起见,我将其复制到此处。

[basic.life] c++ 标准部分中的一段内容指出:

在对象的生命周期开始之前 [...] 如果 [...] 指针用于访问对象的非静态数据成员或调用对象的非静态成员函数,则程序具有未定义的行为

看来,这禁止通过重新解释的引用进行调度,因为该引用不引用活动对象。