use*_*932 10 c++ gcc clang visual-c++ c++11
考虑这个程序:
#include <cstdint>
using my_time_t = uintptr_t;
int main() {
const my_time_t t = my_time_t(nullptr);
}
Run Code Online (Sandbox Code Playgroud)
使用 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
Run Code Online (Sandbox Code Playgroud)
但是 clang (9.0.1) 和 gcc (9.2.1) “吃”了这段代码,没有任何错误。
我喜欢 MSVC 的行为,但是否符合标准?换句话说,它是 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_castand static_cast(带有扩展和组合),但这些都不能std::nullptr_t转换为整数类型。
但是reinterpret_cast<my_time_t>(nullptr)应该会成功,因为[expr.reinterpret.cast]/4说一个类型的值std::nullptr_t可以像 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;
Run Code Online (Sandbox Code Playgroud)
所有内容均符合标准(参考 C++ 草案 n4659)。
nullptr在 [lex.nullptr] 中定义为:
指针字面量是关键字 nullptr。它是 std::nullptr_t 类型的纯右值。[注:...,该类型的纯右值是空指针常量,可以转换为空指针值或空成员指针值。]
即使注释是非规范的,这一注释也清楚地表明,对于标准,nullptr预计将转换为空指针值。
我们稍后在[conv.ptr]中找到:
空指针常量是一个值为 0 的整数文字或类型为 std::nullptr_t 的纯右值。空指针常量可以转换为指针类型;.... 整型的空指针常量可以转换为 std::nullptr_t 类型的纯右值。
这里,标准再次要求的是0可以转换为 astd::nullptr_t并且nullptr可以转换为任何指针类型。
我的理解是,标准没有要求是否nullptr可以直接转换为整数类型。从那时起:
void *Clang 和 gcc 的行为就像涉及中间转换一样。