问题描述
在>了解 Martin Uecker的ICE_P
的标准C11版本之后谓词,我试图在纯C ++中实现它.使用_Generic
选择的C11版本如下:
After reading about the standard C11 version of Martin Uecker's ICE_P
predicate, I tried to implement it in pure C++. The C11 version, making use of _Generic
selection is as follows:
#define ICE_P(x) _Generic((1? (void *) ((x)*0) : (int *) 0), int*: 1, void*: 0)
C ++的明显方法是用模板和decltype
代替_Generic
,例如:
The obvious approach for C++ is to replace _Generic
by a template and decltype
, such as:
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)
但是,它没有通过最简单的测试.为什么不能检测整数常量表达式?
However, it fails the simplest test. Why can't it detect integer constant expressions?
推荐答案
问题很细微.确定条件表达式的指针操作数的复合类型的规范在C ++中类似于C中的规范,因此开始看起来很有希望:
The issue is subtle. The specification for determining the composite type of the conditional expression's pointer operands are similar in C++ to the ones in C, so it starts off looking promising:
7 左值到右值,数组-指向指针和函数指向指针 标准转换是在第二和第三操作数上执行的. 进行这些转换后,应满足以下条件之一:
7 Lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions are performed on the second and third operands. After those conversions, one of the following shall hold:
-
[...]
[...]
第二个操作数和第三个操作数中的一个或两个都具有指针类型;指针转换,函数指针转换和限定 执行转换以将其带入其复合指针 类型(子句[expr]).结果是复合指针类型.
One or both of the second and third operands have pointer type; pointer conversions, function pointer conversions, and qualification conversions are performed to bring them to their composite pointer type (Clause [expr]). The result is of the composite pointer type.
[...]
复合指针类型的归约指定如下:
The reduction to the composite pointer type is specified as follows:
5 两个操作数p1和p2的复合指针类型有 类型分别为T1和T2,其中至少一个是指针或 指向成员类型或std::nullptr_t
的指针是:
5 The composite pointer type of two operands p1 and p2 having types T1 and T2, respectively, where at least one is a pointer or pointer to member type or std::nullptr_t
, is:
- 如果p1和p2均为空指针常量,则
std::nullptr_t
; - 如果p1或p2是空指针常量,则分别为T2或T1;
- 如果T1或T2是指向cv1无效的指针",而另一种类型是指向cv2无效的指针",其中T是对象类型或void,则指向cv12无效的指针", 其中cv12是cv1和cv2的并集;
- [...]
- if both p1 and p2 are null pointer constants,
std::nullptr_t
; - if either p1 or p2 is a null pointer constant, T2 or T1, respectively;
- if T1 or T2 is "pointer to cv1 void" and the other type is "pointer to cv2 T", where T is an object type or void, "pointer to cv12 void", where cv12 is the union of cv1 and cv2;
- [...]
因此,ICE_P
宏的结果取决于我们在依次检查每个子弹后落在上面的哪一颗子弹上.给定我们定义is_ice_helper
的方式,我们知道复合类型不是nullptr_t
,否则我们将碰到第一个项目符号,并且由于缺少模板专用性而将导致错误.因此,我们必须击中项目符号3,使谓词报告为假.一切似乎都取决于空指针常量的定义.
So the result of our ICE_P
macro is determined by which of the bullets above we land one after checking each in order. Given how we defined is_ice_helper
, we know that the composite type is not nullptr_t
, otherwise we'd hit the the first bullet, and will get an error due to the missing template specialization. So we must be hitting bullet number 3, making the predicate report false. It all seems to hinge on the definition of a null pointer constant.
1 一个空指针常量 是具有值的整数文字 零或std::nullptr_t
类型的prvalue.空指针 常量可以转换为指针类型;结果为空 该类型的指针值,并且可以彼此区分 对象指针或函数指针类型的值.这样的转换是 称为空指针转换.两个相同的空指针值 类型应比较相等.空指针常量到的转换 指向cv限定类型的指针是一次转换,而不是 指针转换后跟限定符的顺序 转换.整数类型的空指针常量可以转换 到std::nullptr_t
类型的prvalue.
1 A null pointer constant is an integer literal with value zero or a prvalue of type std::nullptr_t
. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion. A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t
.
由于根据上面的定义,(int*)0
不是空指针常量,因此我们没有资格获得[expr]/5的第一个项目符号.组合类型为 not std::nullptr_t
. (void *) ((x)*0)
既不是空指针常量,也不能将其转换为1.删除强制类型转换(定义中所不允许的内容)使我们留下(x)*0
.这是一个整数常量表达式,值为零.但这不是一个值为零的整数文字! C ++中的空指针常量的定义不同于C中的指针!
Since (int*)0
is not a null pointer constant by the definition above, we do not qualify for the first bullet of [expr]/5. The composite type is not std::nullptr_t
. Neither is (void *) ((x)*0)
a null pointer constant, nor can it be turned into one. Removing the cast (something the definition doesn't allow) leaves us with (x)*0
. This is a integer constant expression with value zero. But it is not an integer literal with value zero! The definition of a null pointer constant in C++ deviates from the one in C!
3 带有值0,或这样的 转换为void *
类型的表达式称为空指针常量.如果 空指针常量将转换为指针类型,结果 指针,称为空指针,保证将不等于与 指向任何对象或函数的指针.
3 An integer constant expression with the value 0, or such an expression cast to type void *
, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
C允许值为零的任意常量表达式形成空指针常量,而C ++则需要整数 literals .鉴于C ++对计算各种文字类型的常量表达式的丰富支持,这似乎是不必要的限制.并使上述方法使ICE_P
在C ++中成为初学者.
C allows arbitrary constant expressions with value zero to form a null pointer constant, while C++ requires integer literals. Given C++'s rich support for computing constant expressions of a variety of literal types, this seems like a needless restriction. And one that makes the above approach to ICE_P
a non-starter in C++.
这篇关于在纯C ++中实现Linux内核的__is_constexpr(ICE_P)宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!