这是一种很实用的编程的方法,可以利用 enum 的特性促使编译器在编译阶段计算常量表达式的值,如下代码:
#include
using namespace std;
// enum hack
template<int a , int b>
struct add{
enum{
x = a + b,
y = a - b
};
};
int main(){
cout << add<5,6>:: x <<endl;
cout << add<5,6>:: y <<endl;
}
生成的中间代码为:
#include
using namespace std;
// enum hack
template<int a, int b>
struct add
{
enum
{
x = a + b,
y = a - b
};
};
/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct add<5, 6>
{
enum
{
x = 5 + 6,
y = 5 - 6
};
};
#endif
int main()
{
std::cout.operator<<(add<5, 6>::x).operator<<(std::endl);
std::cout.operator<<(add<5, 6>::y).operator<<(std::endl);
return 0;
}
从C++17开始,lambda表达式在条件允许的情况下都会隐式声明为constexpr。
从中可以看出,lambdas 表达式在编译阶段就已经计算出了值。但是要注意constexpr表达式退化的问题(就是退化成普通的函数)
// constexpr lambdas 表达式
constexpr int foo()
{
return []()
{ return 58; }();
}
auto get_size = [](int i)
{ return i * 2; };
char buffer1[foo()] = {0};
char buffer2[get_size(5)] = {0};
class X {
public:
static constexpr int num{ 5 };
};
代码中,num是只有声明没有定义的,虽然我们可以通过std::cout << X::num << std::endl输出其结果,但这实际上是编译器的一个小把戏,它将X::num直接替换为了5。如果将输出语句修改为std::cout << &X::num << std::endl,那么链接器会明确报告X::num缺少定义。但是从C++17开始情况发生了变化,static constexpr int num{5}既是声明也是定义,所以在C++17标准中std::cout << &X::num << std::endl可以顺利编译链接,并且输出正确的结果。值得注意的是,对于编译器而言为X::num产生定义并不是必需的,如果代码只是引用了X::num的值,
那么编译器完全可以使用直接替换为值的技巧。只有当代码中引用到变量指针的时候,编译器才会为其生成定义。
让代码能够根据编译时的条件进行实例化,主要是用于模板编程中。但是要特别注意这两点:
if constexpr的条件必须是编译期能确定结果的常量表达式。
条件结果一旦确定,编译器将只编译符合条件的代码块
入下是实现一个带精度的减法。
#include
template<class T> bool is_same_value(T a, T b)
{
if constexpr (std::is_same<T, double>::value) {
if (std::abs(a - b) < 0.0001) {
return true;
}
else {
return false;
}
}
else {
return a == b;
}
}
constexpr声明函数时并不依赖常量表达式上下文环境,在非常量表达式的环境中,函数可以表现为普通函数。不过有时候,我们希望确保函数在编译期就执行计算,对于无法在编译期执行计算的情况则让编译器直接报错。于是在C++20标准中出现了一个新的概念——立即函数。
其实就是可以理解为constexpr的不可退化版本。
std::is_constant_evaluated是C++20新加入标准库的函数,它用于检查当前表达式是否是一个常量求值环境,如果在一个明显常量求值的表达式中,则返回true;否则返回false。包含在在