LeD*_*YoM 21 c++ constexpr c++14 c++17
由于constexpr的扩展版本(我想从C++ 14开始),你可以声明constexpr函数,它们可以用作"真正的"constexpr,即代码在编译时执行或者可以表现为内联函数.那么什么时候可以有这个程序:
#include <iostream>
constexpr int foo(const int s) {
return s + 4;
}
int main()
{
std::cout << foo(3) << std::endl;
const int bar = 3;
std::cout << foo(bar) << std::endl;
constexpr int a = 3;
std::cout << foo(a) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当然,结果是:
7
7
7
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.所以我的问题是:如果函数在编译时或运行时执行,有没有办法(可能是标准的)知道foo(const int s)?
编辑:也可以在运行时知道在编译时是否评估了函数?
Nir*_*man 18
列出的技术有效,但由于它使用static_assert它不是友好的.一个更好的方法(理论上,你会明白我的意思)这样做是检查函数是否是noexcept.为什么?因为,常量表达式始终是noexcept,即使函数没有标记为这样.因此,请考虑以下代码:
template <class T>
constexpr void test_helper(T&& t) {}
#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))
Run Code Online (Sandbox Code Playgroud)
test_helper是constexpr,所以只要它的论点是,它就是一个常量表达式.如果它是一个常量表达式,它将是noexcept,但否则它将不会(因为它没有标记为这样).
所以现在让我们定义一下:
double bar(double x) { return x; }
constexpr double foo(double x, bool b) {
if (b) return x;
else return bar(x);
}
Run Code Online (Sandbox Code Playgroud)
foo只有noexcept当它x是一个常数表达式时b才是真的; 如果布尔值为假,那么我们称之为非constexpr函数,破坏了我们的constexpr-ness.那么,让我们测试一下:
double d = 0.0;
constexpr auto x = IS_CONSTEXPR(foo(3.0, true));
constexpr auto y = IS_CONSTEXPR(foo(3.0, false));
constexpr auto z = IS_CONSTEXPR(foo(d, true));
std::cerr << x << y << z;
Run Code Online (Sandbox Code Playgroud)
编译,太棒了!这给了我们编译时布尔值(不是编译失败),例如可以用于sfinae.
抓到了吗?那么,clang有一个多年的bug,并没有正确处理.但是,确实如此.实例:http://coliru.stacked-crooked.com/a/e7b037932c358149.它应该打印"100".
我认为这样做的规范方法是使用static_assert. static_asserts 在编译时进行评估,因此如果它们的条件为假,它们将破坏构建。
#include <iostream>
constexpr int foo(const int s) {
return s + 4;
}
int main()
{
std::cout << foo(3) << std::endl;
const int bar = 3;
std::cout << foo(bar) << std::endl;
constexpr int a = 3;
std::cout << foo(a) << std::endl;
static_assert(foo(3) == 7, "Literal failed");
static_assert(foo(bar) == 7, "const int failed");
static_assert(foo(a) == 7, "constexpr int failed");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
clang++ -std=c++14 so1.cpp 对我来说编译得很好,表明一切都按预期工作。
C ++ 20引入了is_constant_evaluated在header中定义的<type_traits>,它解决了此问题。
constexpr int foo(int s)
{
if (std::is_constant_evaluated()) // note: not "if constexpr"
/* evaluated at compile time */;
else
/* evaluated at run time */;
}
Run Code Online (Sandbox Code Playgroud)
请注意,此处使用普通if代替if constexpr。如果使用if constexpr,则必须在编译时评估条件,因此is_constant_evaluated始终返回true,从而使测试无用。
在constexpr函数中,您可以判断您是否在c++20constexpr之前的上下文中被评估。从c++20 开始,添加了此功能-会告诉您是否在上下文中被调用。constexpr bool std::is_constant_evaluated()constexpr
在constexpr函数之外,有多种方法可以确定对具有特定参数集的函数的调用是否会在constexpr上下文中进行评估。最简单的方法是在需要constexpr.
假设您的 constexpr 表达式返回一个非空整数或指针类型(包括函数指针):
#define CONSTEXPR_EVAL(...) \
std::integral_constant< \
std::decay_t<decltype(__VA_ARGS__)>, \
__VA_ARGS__ \
>::value
Run Code Online (Sandbox Code Playgroud)
那么CONSTEXPR_EVAL( bar(foo, true) )将无法编译,如果bar(foo, true)不能在编译时进行评估,如果它可以在编译时计算它返回的值。
其他涉及noexcept(在编译时评估的函数是noexcept)的技巧也可以工作(请参阅@NirFriedman 的回答)。
如果您可以使用 C++20,则std::is_constant_evaluated可以完全满足您的需求。std::is_constant_evaluated通常使用编译器内部实现。
这__builtin_is_constant_evaluated在 GCC 和 clang 中被调用,因此您可以围绕它实现自己的“安全”包装器,即使在 C++17 及更低版本中也是如此。
// if C++20, we will need a <type_traits> include for std::is_constant_evaluated
#if __cplusplus >= 202002L
#include <type_traits>
#endif
constexpr bool is_constant_evaluated() {
#if __cplusplus >= 202002L
return std::is_constant_evaluated();
#elif defined(__GNUC__) // defined for both GCC and clang
return __builtin_is_constant_evaluated();
#else
// If the builtin is not available, return a pessimistic result.
// This way callers will implement everything in a constexpr way.
return true;
#endif
}
Run Code Online (Sandbox Code Playgroud)
请注意,此内置函数仍然相对较新(GCC 9.0+),因此您可能还想检测编译器版本。
| 归档时间: |
|
| 查看次数: |
2800 次 |
| 最近记录: |