C++ constexpr是否意味着不例外?
C++ constexpr是否意味着不例外?,c++,inline,c++14,constexpr,noexcept,C++,Inline,C++14,Constexpr,Noexcept,constepr说明符是否暗示函数的noexcept说明符?to对内联说明符说“是”,但让我想知道当前说明符的可能答案。在我看来,答案可能取决于使用constexpr函数的上下文:是常量表达式上下文还是运行时上下文,即编译时函数的所有参数是否已知 我原以为答案是“是”,但事实并非如此 constexpr bool f(int) noexcept { return true; } constexpr bool g(int) { return true; } static_ass
constepr
说明符是否暗示函数的noexcept
说明符?to对内联说明符说“是”,但让我想知道当前说明符的可能答案。在我看来,答案可能取决于使用constexpr
函数的上下文:是常量表达式上下文还是运行时上下文,即编译时函数的所有参数是否已知
我原以为答案是“是”,但事实并非如此
constexpr
bool f(int) noexcept
{
return true;
}
constexpr
bool g(int)
{
return true;
}
static_assert(noexcept(f(1)));
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour
#include <cassert>
#include <cstdlib>
int
main(int argc, char * [])
{
assert(noexcept(f(argc)));
assert(noexcept(g(argc)));
return EXIT_SUCCESS;
}
constexpr
boolf(int)无例外
{
返回true;
}
常量表达式
布尔g(内部)
{
返回true;
}
静态断言(noexcept(f(1));
静态断言(noexcept(g(2));//注释此行以检查运行时行为
#包括
#包括
int
main(int argc,char*[])
{
断言(noexcept(f(argc));
断言(noexcept(g(argc));
返回退出成功;
}
的
不例外
:
如果表达式包含对没有非抛出异常规范的任何类型函数的[…]调用,则结果为false,除非它是常量表达式
另外,关于constexpr
,请注意:
noexcept运算符对于常量表达式始终返回true
在任何情况下,它似乎都不意味着constepr
说明符强制为所包围的表达式指定noexcept
说明符,正如有人在注释中用反例所示,您也验证了这一点
无论如何,从文档中,有以下关于noexcept
和constexpr
之间关系的有趣注释:
由于noexcept运算符始终为常量表达式返回true,因此可以使用它检查constexpr函数的特定调用是否采用常量表达式分支
编辑:带有GCC的示例
感谢@hvd对GCC关于我上一次报价的有趣评论/示例
constexpr int f(int i) {
return i == 0 ? i : f(i - 1);
}
int main() {
noexcept(f(512));
return noexcept(f(0)) == noexcept(f(0));
}
上面的代码返回0
,并警告语句noexcept(f(512))
无效。
注释掉假定无效的语句后,返回值变为1
编辑:已知的叮当声bug
再次感谢@hvd也提供了link,这是关于问题中提到的代码的一个众所周知的bug
从错误报告中引用:
<>〈C++〉,[第1章除外] P3:
“如果在可能求值的上下文中,表达式将包含对没有非抛出异常规范(15.4)的函数、成员函数、函数指针或成员函数指针的可能求值调用,则noexcept运算符的结果为false,除非该调用是常量表达式(5.19)”
我们不执行最后一句话
不,一般来说不是
可以在允许抛出异常的非constepr上下文中调用constexpr函数(当然,手动将其指定为noexcept(true)
)
但是,作为常量表达式的一部分(例如在您的示例中),它的行为应该像指定为noexcept(true)
(当然,如果表达式的求值将导致抛出异常,这不会导致调用std::terminate
,因为程序尚未运行,而是会导致编译时错误)
正如我所料,您的示例不会触发MSVC和g++的静态断言。我不确定这是clang中的错误还是我对标准中的某些内容理解错误。您可以在constexpr函数中抛出异常。它的设计是这样的,以便实现者可以向编译器指示错误请考虑以下功能:
constexpr int fast_sqrt(int x) {
return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x);
}
constexpr int fast\u sqrt(int x){
返回(x<0)?抛出无效的\u域():fast\u sqrt\u impl(x);
}
在这种情况下,如果x为负值,我们需要立即停止编译,并通过编译器错误向用户指出问题。这遵循了编译器错误优于运行时错误(快速失败)的思想
C++标准在(5.20)中说明了这一点:
条件表达式e是一个核心常量表达式,除非根据
抽象机器(1.9)将计算以下表达式之一:
-投掷表情(5.17)
不,这是不可能的,因为不是constexpr函数的每一个inovocation都必须能够作为核心常量表达式的子表达式进行计算。我们只需要一个参数值就可以实现这一点。因此,只要有一个不调用该分支的参数值,constexpr函数就可以包含一个throw语句
C++14标准草案部分7.1.5
constexpr说明符[dcl.constexpr]介绍了这一点,它告诉我们在constexpr函数中允许什么:
constexpr int fast_sqrt(int x) {
return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x);
}
constexpr函数的定义应满足以下约束条件:
- 它不应是虚拟的(10.3)
- 其返回类型应为文字类型
- 其每个参数类型应为文字类型
- 其函数体应为=delete、=default或不包含
- asm定义
- goto声明
- 试块,或
- 非文字类型变量或静态或线程存储持续时间变量的定义,或
不执行初始化
正如我们所看到的,这并不禁止抛出,事实上,自从提案成为C++14的一部分以来,几乎没有禁止
下面我们看到的规则是,如果至少有一个参数值存在,那么constexpr函数的格式是正确的,这样它就可以作为子表达式进行计算
#include <iostream>
constexpr int f(bool b) { return b ? throw 0 : 0; }
int main() {
std::cout << noexcept( f(1) ) << "\n"
<< noexcept( f(0) ) << "\n" ;
}
0
1