将指针/引用转换为固定数组大小是否合法

Hum*_*ler 23 c++ language-lawyer c++11

根据 C++ 标准,将指向固定数组(例如T(*)[N]T(&)[N])的指针或引用转换为指向相同类型和 CV 限定(例如T(*)[M]T(&)[M])的较小固定数组的指针或引用是否合法?

基本上,对于T(无论布局类型)的所有实例化,这是否总是格式良好的

void consume(T(&array)[2]);

void receive(T(&array)[6])
{
  consume(reinterpret_cast<T(&)[2]>(array));   
}
Run Code Online (Sandbox Code Playgroud)

我没有看到任何对这是有效转换的引用:

然而,似乎所有主要编译器都接受这一点并生成正确的代码,即使在使用T = std::string( compiler explorer)时进行了优化(如果它是未定义的行为,这证明不是很多)

我的理解是,根据类型系统,这应该是非法的,因为T[2]从未真正创建过对象,这意味着对的引用T(&)[2]将是无效的。


我将这个问题标记为因为这是我对答案最感兴趣的版本,但我很想知道这个答案在较新版本中是否有所不同。

Dav*_*ing 8

除了no,在任何语言版本中,这里没什么好说的:类型根本不相关。C++20 确实允许从T (*)[N]to转换T (*)[](对于引用也是如此),但这并不意味着您可以N等效地对待两个不同的s。最接近此规则的“引用”是 [conv.array]/1(“结果是指向数组第一个元素的指针。”,T[2]在您的示例中不存在)和[defns.undefined] 中的注释(“当本文档省略任何明确的行为定义时,可能会出现未定义的行为”)。

编译器没有“抓住”你的部分原因是这样的reinterpret_casts可以有效地返回到一个对象的真实类型,然后另一个reinterpret_cast用于通过一个需要指针或对不同类型的引用的接口“偷偷摸摸”它(但不使用它作为那种类型!)。这意味着给定的代码是合法的,但是明显的 forconsume和 caller for定义receive一起导致未定义的行为。(另一部分是优化器通常会单独留下始终未定义的代码,除非它可以消除分支。)

  • 对于标准布局类型,我指的是 [`expr.reinterpret.cast` §5.2.10/7](https://timsong-cpp.github.io/cppwp/n3337/expr.reinterpret.cast#7)声明只要“T1”和“T2”是具有兼容对齐方式的标准布局对象,您就可以将“T1”的指针“reinterpret_cast”到“T2”。也就是说,它只表明指针转换是有效的——而不是指针本身可以被使用。据我所知,公共初始序列只能在“union”的情况下访问,但不能在“reinterpret_cast”的情况下访问 (3认同)