可以将nullptr转换为uintptr_t吗?不同的编译器不同意


10

考虑以下程序:

#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)可以“吃掉”这段代码,而不会出现任何错误。

我喜欢MSVC行为,但是是否已通过标准确认?换句话说,这是clang / gcc中的bug还是可以从gcc / clang中解释这是正确的行为的标准?


2
我将其作为函数样式转换的副本初始化读取。然后,编译器将其解释为C ++强制转换 “即使无法编译”。编译器之间关于
强制转换的

据我所知,MSVC v19.24不支持C ++ 11模式。您是说C ++ 14还是C ++ 17?
核桃核桃

Answers:


5

我认为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_castconst_caststatic_cast(带有扩展名和组合形式),但是这些都不可以强制转换std::nullptr_t为整数类型。

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

如果使用强制转换表示法而不是功能表示法,则MSVC允许转换是非常奇怪的:

const my_time_t t = (my_time_t)nullptr;

1
是的 请注意,static_cast在某些情况下,特别是打算捕获C样式的转换梯形(例如,将C样式的转换转换为模棱两可的基体是不正确的形式,static_cast而不是reinterpret_cast),但在此不适用。
TC

my_time_t(nullptr)根据定义,它与相同(my_time_t)nullptr,因此MSVC接受一个并拒绝另一个肯定是错误的。
Richard Smith

2

尽管我在该工作草案C ++标准(自2014年起)中没有明确提到禁止从整数类型转换,但也没有提及允许这种转换!std::nullptr_t

然而,从转换的情况下std::nullptr_t,以bool 明确提及:

4.12布尔转换
算术,无作用域枚举,指针或指向成员类型的指针的prvalue可以转换为bool类型的prvalue。零值,空指针值或空成员指针值将转换为false;其他任何值都将转换为true。对于直接初始化(8.5),可以将std :: nullptr_t类型的prvalue转换为bool类型的prvalue。结果值为false。

此外,该文档草案中唯一std::nullptr_t提到从转换为整数类型的地方是在“ reinterpret_cast”部分:

5.2.10重新解释强制 转换
...
(4)可以将指针显式转换为足够大的整数类型以容纳它。映射功能是实现定义的。[注意:对于那些了解底层机器的寻址结构的人来说,这并不奇怪。—结束说明] std :: nullptr_t类型的值可以转换为整数类型;该转换与(void *)0转换为整数类型具有相同的含义和有效性。[注意:reinterpret_cast不能用于将任何类型的值转换为std :: nullptr_t类型。—尾注]

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

编辑:但是,您对“功能符号转换”的使用实际上可能暗示相反的意思!该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样式的强制转换。


0

全部符合标准(C ++参考草案n4659)。

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

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

即使注释不是规范性的,这一点也很清楚,对于标准而言,nullptr预期将其转换为空指针值。

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

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

在此,标准再次要求0可以将其转换为a std::nullptr_t,并且nullptr可以将其转换为任何指针类型。

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

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

1
我认为这是错误的。一些空指针常量是值为零的整数文字,但这nullptr不是因为它具有非整数类型std::nullptr_t。0可以转换为std::nullptr_t值,但不能转换为文字nullptr。这都是有意的,std::nullptr_t是一种更受限制的类型,可防止意外转换。
MSalters

@MSalters:我认为你是对的。我想改写它,做错了。我已经用您的评论编辑了我的帖子。感谢您的帮助。
Serge Ballesta
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.