Sha*_*our 46 c++ sfinae undefined-behavior constexpr c++11
我正在研究核心常量表达式*中允许的内容,这在C++标准草案的5.19 常量表达式第2段中有所描述:
条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2),但是未评估的逻辑AND(5.14),逻辑OR(5.15)和条件(5.16)操作的子表达式不考虑[注意:重载的运算符调用函数.-end note]:
并列出随后的子弹中的排除项并包括(强调我的):
- 具有未定义行为的操作 [注意:包括,例如,有符号整数溢出(第5条),某些指针算术(5.7),除零(5.6)或某些移位操作(5.8) - 结束注释];
嗯?为什么常量表达式需要此子句来涵盖未定义的行为?常量表达式是否有一些特殊的东西需要未定义的行为才能在排除中进行特殊划分?
拥有这个条款是否给了我们没有它的任何优势或工具?
作为参考,这看起来像广义常量表达式提案的最新修订版.
Sha*_*our 39
措辞实际上是缺陷报告#1313的主题,其中说:
常量表达式的要求当前不应该但是应该排除具有未定义行为的表达式,例如当指针不指向同一数组的元素时的指针算法.
该决议是我们现在的措辞,所以这显然是有意的,那么这给了我们什么样的工具呢?
让我们看看当我们尝试使用包含未定义行为的表达式创建constexpr变量时会发生什么,我们将使用以下所有示例.这段代码(现场直播):clang
constexpr int x = std::numeric_limits<int>::max() + 1 ;
Run Code Online (Sandbox Code Playgroud)
产生以下错误:
error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = std::numeric_limits<int>::max() + 1 ;
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: value 2147483648 is outside the range of representable values of type 'int'
constexpr int x = std::numeric_limits<int>::max() + 1 ;
^
Run Code Online (Sandbox Code Playgroud)
这段代码(现场直播):
constexpr int x = 1 << 33 ; // Assuming 32-bit int
Run Code Online (Sandbox Code Playgroud)
产生此错误:
error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = 1 << 33 ; // Assuming 32-bit int
^ ~~~~~~~
note: shift count 33 >= width of type 'int' (32 bits)
constexpr int x = 1 << 33 ; // Assuming 32-bit int
^
Run Code Online (Sandbox Code Playgroud)
这个代码在constexpr函数中有未定义的行为:
constexpr const char *str = "Hello World" ;
constexpr char access( int index )
{
return str[index] ;
}
int main()
{
constexpr char ch = access( 20 ) ;
}
Run Code Online (Sandbox Code Playgroud)
产生此错误:
error: constexpr variable 'ch' must be initialized by a constant expression
constexpr char ch = access( 20 ) ;
^ ~~~~~~~~~~~~
note: cannot refer to element 20 of array of 12 elements in a constant expression
return str[index] ;
^
Run Code Online (Sandbox Code Playgroud)
嗯,这是非常有用的编译器可以检测到未定义的行为在constexpr,或者至少是clang认为是不确定的.注意,gcc行为相同,除非在右移和左移的未定义行为的情况下,gcc通常会在这些情况下产生警告,但仍然将表达式视为常量.
我们可以通过SFINAE使用此功能来检测添加表达式是否会导致溢出,以下设计的示例受到dyp的巧妙答案的启发:
#include <iostream>
#include <limits>
template <typename T1, typename T2>
struct addIsDefined
{
template <T1 t1, T2 t2>
static constexpr bool isDefined()
{
return isDefinedHelper<t1,t2>(0) ;
}
template <T1 t1, T2 t2, decltype( t1 + t2 ) result = t1+t2>
static constexpr bool isDefinedHelper(int)
{
return true ;
}
template <T1 t1, T2 t2>
static constexpr bool isDefinedHelper(...)
{
return false ;
}
};
int main()
{
std::cout << std::boolalpha <<
addIsDefined<int,int>::isDefined<10,10>() << std::endl ;
std::cout << std::boolalpha <<
addIsDefined<int,int>::isDefined<std::numeric_limits<int>::max(),1>() << std::endl ;
std::cout << std::boolalpha <<
addIsDefined<unsigned int,unsigned int>::isDefined<std::numeric_limits<unsigned int>::max(),std::numeric_limits<unsigned int>::max()>() << std::endl ;
}
Run Code Online (Sandbox Code Playgroud)
结果(见它直播):
true
false
true
Run Code Online (Sandbox Code Playgroud)
标准要求这种行为并不明显,但霍华德·亨南特的评论显然确实如此:
[...]并且也是constexpr,意味着UB在编译时被捕获
更新
不知怎的,我错过了第695期的constexpr函数中的编译时计算错误,这些错误围绕着5第4节的措辞(过去强调我的未来):
如果在评估表达式期间,结果未在数学上定义或未在其类型的可表示值范围内,则行为未定义,除非出现需要整数常量表达式的表达式(5.19 [expr.const]) ),在这种情况下,该程序是不正确的.
接着说:
旨在作为"在编译时评估"的可接受的标准附录,这个概念不是由标准直接定义的.目前尚不清楚这种配方是否足以涵盖constexpr功能.
后来的说明说:
[...]在编译时想要诊断错误与不诊断在运行时实际不会发生的错误之间存在紧张关系.[...] CWG的共识是,像1/0这样的表达应该只是被认为是非常数; 在需要常量表达的上下文中使用表达式会导致任何诊断.
如果我正确阅读,则确认其意图是能够在需要常量表达的上下文中在编译时诊断未定义的行为.
我们不能肯定地说这是意图,但强烈暗示它是.在怎么区别clang和gcc对待不确定的变化并留下怀疑的余地.
我提交了一个gcc错误报告:右移和左移未定义的行为不是constexpr中的错误.虽然看起来这符合要求,但确实打破了SFINAE,我们可以从我的回答中看到它是否是一个符合编译器的扩展,将非constexpr标准库函数视为constexpr?委员会认为SFINAE用户可观察到的实施差异似乎是不可取的.
Ben*_*igt 21
当我们谈论未定义的行为时,重要的是要记住标准为这些情况留下了未定义的行为.它并不禁止实施更强有力的保证.例如,一些实现可以保证有符号整数溢出包围,而其他实现可以保证饱和.
要求编译器处理涉及未定义行为的常量表达式将限制实现可以做出的保证,限制它们产生一些没有副作用的值(标准称为不确定值).这排除了现实世界中发现的许多延伸保证.
例如,一些实现或伴随标准(即POSIX)可以将积分除以零的行为定义为生成信号.如果表达式是在编译时计算的,那么这会产生副作用.
因此,这些表达式在编译时被拒绝,以避免在执行环境中丢失副作用.
还有一点是从常量表达式中排除未定义的行为:根据定义,常量表达式应该由编译器在编译时进行评估.允许常量表达式来调用未定义的行为将允许编译器本身显示未定义的行为.而且,编译硬盘驱动器的编译器因为编译了一些恶意代码而不是你想拥有的.
| 归档时间: |
|
| 查看次数: |
2752 次 |
| 最近记录: |