可以将nultptr转换为uintptr_t吗?不同的编纂者不同意



考虑这个程序:

#include <cstdint>
using my_time_t = uintptr_t;
int main() {
const my_time_t t = my_time_t(nullptr);
}

它无法使用msvc v19.24:进行编译

<source>(5): error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'my_time_t'
<source>(5): note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
<source>(5): error C2789: 't': an object of const-qualified type must be initialized
<source>(5): note: see declaration of 't'
Compiler returned: 2

但是clang(9.0.1)和gcc(9.2.1)"吃掉"了这个代码,没有任何错误。

我喜欢微软风投的行为,但它是否得到了标准的证实?换句话说,这是clang/gcc中的错误,还是可以解释gcc/clang认为这是正确行为的标准?

在我看来,MSVC的行为不符合标准。

我的答案是基于C++17(草案N4659),但C++14和C++11的措辞相当。

my_time_t(nullptr)后缀表达式,并且由于my_time_t是类型,而(nullptr)是带括号的初始值设定项列表中的单个表达式,因此它完全等效于显式强制转换表达式。([expr.type.conv]/2)

显式强制转换尝试一些不同的特定C++强制转换(带有扩展),特别是reinterpret_cast。([expr.cast]/4.4)在reinterpret_cast之前尝试的强制转换是const_caststatic_cast(带扩展名,也有组合),但它们都不能将std::nullptr_t强制转换为整型。

但是reinterpret_cast<my_time_t>(nullptr)应该成功,因为[expr.relpretere.cast]/4说std::nullptr_t类型的值可以像reinterpret_cast<my_time_t>((void*)0)一样转换为积分类型,这是可能的,因为my_time_t = std::uintptr_t应该是一个足够大的类型,可以表示所有指针值,并且在这种情况下,相同的标准段落允许void*转换为积分型。

特别奇怪的是,如果使用强制转换表示法而不是函数表示法,MSVC确实允许转换:

const my_time_t t = (my_time_t)nullptr;

尽管我在本C++标准工作草案(2014年)中找不到明确的提及禁止从std::nullptr_t转换为整型,但也没有提及允许这样的转换!

然而,明确提到了从std::nullptr_tbool的转换情况:

4.12布尔转换
算术、无范围枚举、指针或指向成员类型的指针的prvalue可以转换为布尔类型的prvalue。零值、null指针值或null成员指针值被转换为false;任何其他值都转换为是的。对于直接初始化(8.5),类型为的prvaluestd::nullptr_t可以转换为布尔类型的prvalue;这个结果值为false。

此外,本文件草案中提到从std::nullptr_t转换为积分类型的唯一位置在"interpret_cast"部分:

5.2.10重新解释演员阵容

(4)指针可以显式转换为任何足以容纳它的整数类型。映射函数为实现定义。[注:那些知道底层机器的寻址结构的人--end note]std::nullptr_t类型的值可以转换为积分型;转换与将(void*)0转换为整型。[注:一个interpret_cast不能用于将任何类型的值转换为类型std::nullptr_t。--尾注]

因此,根据这两个观察结果,可以(IMHO)合理地推测MSVC编译器是正确的。

EDIT:然而,您使用的"函数表示法cast"实际上可能会提出相反的建议!MSVC编译器使用C样式的强制转换没有问题,例如:

uintptr_t answer = (uintptr_t)(nullptr);

但是(就像在你的代码中一样),它抱怨这个:

uintptr_t answer = uintptr_t(nullptr); // error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'uintptr_t'

然而,来自同一标准草案:

5.2.3显式类型转换(函数表示法)
(1)后面跟着简单类型说明符(7.1.6.2)或类型名说明符(14.6)通过带括号的表达式列表构造指定的值在给定表达式列表的情况下键入。如果表达式列表是单个表达式,类型转换表达式等效(在定义,如果在含义上定义)表达式(5.4).

"对应的强制转换表达式(5.4)"可以指C样式的强制转换。

所有都符合标准(参考C++草案n4659)。

nullptr在[lex.nullptr]中定义为:

指针文本是关键字nullptr。它是std::nullptr_t类型的prvalue。[注意:…,此类型的prvalue为空指针常量,可以转换为空指针值或空成员指针值。]

即使注释是非规范性的,这一注释也清楚地表明,对于标准,nullptr预计将转换为空指针值。

我们稍后在[conv.ptr]中找到:

空指针常量是值为零的整数文字或std::nullptr_t类型的prvalue。A.空指针常量可以转换为指针类型。。。。整型的空指针常量可以转换为std::nullptr_t类型的prvalue。

这里,标准再次要求0可以转换为std::nullptr_t,并且nullptr可以转换为任何指针类型。

我的理解是,该标准没有要求nullptr是否可以直接转换为积分类型。从那时起:

  • MSVC有严格的阅读并禁止转换
  • Clang和gcc的行为就像涉及中间的void *转换一样

最新更新