在读到 -- Martin的ICE_P
谓词的标准C11版本--之后,我尝试用纯C++实现它。使用C11选择的_Generic
版本如下:
#define ICE_P(x) _Generic((1? (void *) ((x)*0) : (int *) 0), int*: 1, void*: 0)
C++的明显方法是用模板和decltype
替换_Generic
,例如:
template<typename T> struct is_ice_helper;
template<> struct is_ice_helper<void*> { enum { value = false }; };
template<> struct is_ice_helper<int*> { enum { value = true }; };
#define ICE_P(x) (is_ice_helper<decltype(1? (void *) ((x)*0) : (int *) 0)>::value)
然而,它失败了最简单测试。为什么它不能检测整数常量表达式?
发布于 2019-04-21 06:24:46
这个问题很微妙。在C++中确定条件表达式指针操作数的组合类型的规范与C中的类似,因此它开始看起来很有希望:
(N4659) expr.cond 在第二和第三个操作数上执行7值到rvalue、数组到指针以及函数到指针的标准转换.在这些转换之后,应持有下列一项规定:
组合指针类型的简化如下所示:
(N4659) expr 5两个操作数p1和p2的复合指针类型分别具有T1和T2类型,其中至少有一个是指向成员类型或
std::nullptr_t
的指针或指针,为:
std::nullptr_t
;因此,我们的ICE_P
宏的结果是由我们上面的哪一个子弹决定的,在检查每个子弹的顺序后,我们就会着陆一个。考虑到我们如何定义is_ice_helper
,我们知道复合类型不是nullptr_t
,否则我们就会命中第一个项目,并且会因为缺少模板专门化而得到一个错误。因此,我们必须命中3号符号,使谓词报告为假。这一切似乎都取决于空指针常量的定义。
(N4659) conv.ptr (重点雷) 1空指针常量是值为零的整数文本或
std::nullptr_t
类型的prvalue。可以将空指针常量转换为指针类型;结果是该类型的空指针值,并且与对象指针或函数指针类型的每个其他值都有区别。这种转换称为空指针转换。相同类型的两个空指针值应比较相等。将空指针常量转换为指针到cv限定类型的转换是一次转换,而不是指针转换的顺序,然后是限定转换。整数类型的空指针常量可以转换为std::nullptr_t
类型的prvalue。
由于根据上面的定义,(int*)0
不是空指针常量,所以我们不限定expr/5的第一个符号,复合类型不是std::nullptr_t
。(void *) ((x)*0)
既不是空指针常量,也不能转化为空指针常量。删除强制转换(定义不允许的内容)将留给我们(x)*0
。这是一个值为零的整数常量表达式。但它不是值为零的整数字面值!C++中空指针常数的定义与C中的定义不同!
(N1570) 6.3.2.3指针 3.值为0的整数常量表达式或转换为
void *
类型的此类表达式称为空指针常量。如果将空指针常量转换为指针类型,则保证结果指针(称为空指针)与指向任何对象或函数的指针不相等。
C允许值为零的任意常量表达式形成空指针常量,而C++则需要整数字面值。鉴于C++对计算各种文字类型的常量表达式的丰富支持,这似乎是一个不必要的限制,这使得上述ICE_P
方法在C++中是不可能实现的。
https://stackoverflow.com/questions/55780426
复制相似问题