101*_*010 17 c++ gcc clang constexpr c++11
考虑以下代码:
struct foo {
static constexpr const void* ptr = reinterpret_cast<const void*>(0x1);
};
auto main() -> int {
return 0;
}
Run Code Online (Sandbox Code Playgroud)
上面的例子在g ++ v4.9(Live Demo)中编译得很好,但它无法在clang v3.4(Live Demo)中编译并生成以下错误:
错误:constexpr变量'ptr'必须由常量表达式初始化
问题:
根据标准,哪两个编译器是正确的?
声明这种表达的正确方法是什么?
Sha*_*our 16
TL; DR
clang这是正确的,这是已知的gcc错误.您可以intptr_t在需要使用值时使用替代和强制转换,或者如果它不可行则可以使用这两者gcc并clang支持一些文档化的解决方法,应该允许您的特定用例.
细节
clang如果我们去C++ 11标准部分草案,那么在这一部分是正确的.5.19 常量表达式第2段说:
条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式[...]
并包括以下项目:
- reinterpret_cast(5.2.10);
一个简单的解决方案是使用intptr_t:
static constexpr intptr_t ptr = 0x1;
Run Code Online (Sandbox Code Playgroud)
然后在需要使用时再投射:
reinterpret_cast<void*>(foo::ptr) ;
Run Code Online (Sandbox Code Playgroud)
将它留在那可能很诱人,但这个故事虽然变得更有趣.这是知道的,仍然是开放的gccbug见Bug 49171:[C++ 0x] [constexpr]常量表达式支持reinterpret_cast.从讨论中gcc可以清楚地看出,开发人员有一些明确的用例:
我相信我在C++ 03中可用的常量表达式中找到了reinterpret_cast的一致用法:
Run Code Online (Sandbox Code Playgroud)//---------------- struct X { X* operator&(); }; X x[2]; const bool p = (reinterpret_cast<X*>(&reinterpret_cast<char&>(x[1])) - reinterpret_cast<X*>(&reinterpret_cast<char&>(x[0]))) == sizeof(X); enum E { e = p }; // e should have a value equal to 1 //----------------基本上这个程序演示了这个技术,C++ 11库函数addressof基于并因此无条件地从核心语言中的常量表达式中排除reinterpret_cast 会使这个有用的程序无效并且使得无法将addressof声明为constexpr函数.
但是无法为这些用例编写异常,请参阅已关闭的问题1384:
虽然在C++ 03中的地址常量表达式中允许使用reinterpret_cast,但是这种限制已在某些编译器中实现,并且未证明会破坏大量代码.CWG认为处理指纹改变的指针的复杂性(指针算术和取消引用不允许这样的指针)超过了放宽当前限制的可能效用.
BUT显然gcc并clang支持一点点记录扩展,它允许使用非常量表达式常量折叠__builtin_constant_p(EXP)等以下表达式由两个接受gcc和clang:
static constexpr const void* ptr =
__builtin_constant_p( reinterpret_cast<const void*>(0x1) ) ?
reinterpret_cast<const void*>(0x1) : reinterpret_cast<const void*>(0x1) ;
Run Code Online (Sandbox Code Playgroud)
为此找到文档几乎是不可能的,但是这个llvm提交提供了丰富的信息,下面的代码片段提供了一些有趣的阅读:
- 支持gcc __builtin_constant_p()?...:...在C++ 11中折叠黑客
和:
+ // __builtin_constant_p?:是神奇的,永远是一个潜在的常数.
和:
- //这个宏强制它的参数是不变的,即使它不是
- //否则是一个常量表达式.
define fold(x)(__ builtin_constant_p(x)?(x):( x))
我们可以在gcc-patches电子邮件中找到关于此功能的更正式的解释:C常量表达式,VLA等修复,其中说:
此外,实现中__builtin_constant_p调用作为条件表达式条件的规则比正式模型中的规则更宽松:条件表达式的选定一半完全折叠而不考虑它是否是正式表达式,因为__builtin_constant_p完全测试了折叠论证本身.
Ker*_* SB 12
Clang是对的.重新解释的结果永远不是一个常量表达式(参见C++ 11 5.19/2).
常量表达式的目的是可以将它们作为值进行推理,并且值必须是有效的.你写的东西不是一个有效的指针(因为它不是一个对象的地址,或者通过指针算术与一个对象的地址相关),所以不允许你将它用作一个常量表达式.如果您只想存储该号码1,请将其存储为a uintptr_t并在使用站点进行重新解释.
顺便说一句,要详细说明"有效指针"的概念,请考虑以下constexpr指针:
constexpr int const a[10] = { 1 };
constexpr int * p1 = a + 5;
constexpr int const b[10] = { 2 };
constexpr int const * p2 = b + 10;
// constexpr int const * p3 = b + 11; // Error, not a constant expression
static_assert(*p1 == 0, ""); // OK
// static_assert(p1[5] == 0, ""); // Error, not a constant expression
static_assert(p2[-2] == 0, ""); // OK
// static_assert(p2[1] == 0, ""); // Error, "p2[1]" would have UB
static_assert(p2 != nullptr, ""); // OK
// static_assert(p2 + 1 != nullptr, ""); // Error, "p2 + 1" would have UB
Run Code Online (Sandbox Code Playgroud)
这两个p1和p2是常量表达式.但是指针运算的结果是否是常量表达式取决于它是否不是UB!如果允许reinterpret_casts的值为常量表达式,则这种推理基本上是不可能的.
| 归档时间: |
|
| 查看次数: |
3691 次 |
| 最近记录: |