// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
我觉得如果我写了一个返回字面值的函数,并且在代码审查中遇到了这个问题,有人会告诉我,我应该声明一个常量值,而不是写 return 5。
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
假设它执行的操作稍微复杂一些。
constexpr int MeaningOfLife ( int a, int b ) { return a * b; }
const int meaningOfLife = MeaningOfLife( 6, 7 );
现在你有了一个可以被计算为常量的东西,同时保持良好的可读性,并允许比仅将常量设置为数字更复杂的处理。
它基本上提供了一种良好的维护性,因为它变得更加明显你正在做什么。例如,考虑max(a, b)
:
template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }
这是一个相当简单的选择,但这意味着如果您使用常量值调用max
函数,它将在编译时显式计算而不是运行时。
另一个很好的例子是DegreesToRadians
函数。每个人都发现度数比弧度更容易理解。虽然您可能知道180度等于3.14159265(圆周率)弧度,但以下写法更清晰:
const float oneeighty = DegreesToRadians( 180.0f );
这里有很多好的信息:
constexpr
并不是作为一种告诉实现程序可以在需要常量表达式的上下文中进行评估的方式而引入的;符合规范的实现在C++11之前就已经能够证明这一点。
实现无法证明的是某段代码的意图:
constexpr
,世界会是什么样子?假设你正在开发一个库,并意识到你想要能够计算区间(0,N]
中每个整数的总和。
int f (int n) {
return n > 0 ? n + f (n-1) : n;
}
如果在编译时已知传递的参数,编译器可以轻松证明上述函数在常量表达式中是可调用的;但是你并没有声明这个意图 - 它只是碰巧是这样的情况。
现在有人来读你的函数,进行与编译器相同的分析;"哦,这个函数可以在常量表达式中使用!",并编写了以下代码片段。
T arr[f(10)]; // freakin' magic
f
时应该缓存结果;谁会想要一遍又一遍地计算相同的值集合呢?int func (int n) {
static std::map<int, int> _cached;
if (_cached.find (n) == _cached.end ())
_cached[n] = n > 0 ? n + func (n-1) : n;
return _cached[n];
}
constexpr
?
constexpr的主要用途是声明意图。constexpr
- 那就意味着它从未被设计为在常量表达式中使用;即使它被使用了,我们仍然依赖编译器来诊断这样的上下文(因为它会忽略我们的意图)。constexpr
您获得的是一种行为保证,就像const
一样。 - Tomáš Zatoint f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)];
?我无法在任何地方编译它? - Jer考虑到某些原因,std::numeric_limits<T>::max()
是一种方法。在这里使用constexpr
会很有益。
另一个例子:您想声明一个与另一个数组大小相同的C数组(或std::array
)。目前的做法如下:
int x[10];
int y[sizeof x / sizeof x[0]];
int y[size_of(x)];
由于使用 constexpr
,您可以:
template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
return N;
}
constexpr
会强制编译器尽可能让函数返回一个编译时的值(如果可以的话)。 - deft_codeconstexpr
,它无法用作数组大小声明,也无法作为模板参数,无论函数调用的结果是否为编译时常量。这两种情况基本上是 constexpr
的唯一用例,但至少模板参数的用例相当重要。 - Konrad Rudolphconstexpr
函数非常好用,是C++的一个很棒的补充。然而,你说得对,它所解决的大多数问题都可以通过宏来不太优雅地解决。
然而,constexpr
的一个用途在C++03中没有等价物,那就是有类型的常量。
// This is bad for obvious reasons.
#define ONE 1;
// This works most of the time but isn't fully typed.
enum { TWO = 2 };
// This doesn't compile
enum { pi = 3.1415f };
// This is a file local lvalue masquerading as a global
// rvalue. It works most of the time. But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;
// This is a true constant rvalue
constexpr float pi = 3.1415f;
// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor
struct A
{
static const int four = 4;
static const int five = 5;
constexpr int six = 6;
};
int main()
{
&A::four; // linker error
&A::six; // compiler error
// EXTREMELY subtle linker error
int i = rand()? A::four: A::five;
// It not safe use static const class variables with the ternary operator!
}
//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;
four
还是five
都不在作用域内。 - Steven Luenum class
类型,它解决了一些枚举类型的问题。 - ninMonkeyconstexpr
在具有强大的编译时表达式求值系统的编译器中更具体地有用。在这个领域,C++确实没有对手。(在我看来,这是对C++11的高度赞扬) - Warren P来自斯特劳斯特鲁普在“Going Native 2012”演讲中的内容:
template<int M, int K, int S> struct Unit { // a unit in the MKS system
enum { m=M, kg=K, s=S };
};
template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
explicit Value(double d) : val(d) {} // construct a Value from a double
};
using Speed = Value<Unit<1,0,-1>>; // meters/second type
using Acceleration = Value<Unit<1,0,-2>>; // meters/second/second type
using Second = Unit<0,0,1>; // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second
constexpr Value<Second> operator"" s(long double d)
// a f-p literal suffixed by ‘s’
{
return Value<Second> (d);
}
constexpr Value<Second2> operator"" s2(long double d)
// a f-p literal suffixed by ‘s2’
{
return Value<Second2> (d);
}
Speed sp1 = 100m/9.8s; // very fast for a human
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit)
Acceleration acc = sp1/0.5s; // too fast for a human
另一个尚未提到的用途是constexpr
构造函数。这允许创建编译时常量,它们不需要在运行时初始化。
const std::complex<double> meaning_of_imagination(0, 42);
3.14D + 42_i;
我刚开始将一个项目转换成c++11,并且遇到了一个非常适合使用constexpr的情况,它可以清理掉执行同一操作的替代方法。这里关键的一点是:只有在函数被声明为constexpr时才能将其放入数组大小声明中。在我所涉及的代码区域中,我可以看到很多情况下都会非常有用。
constexpr size_t GetMaxIPV4StringLength()
{
return ( sizeof( "255.255.255.255" ) );
}
void SomeIPFunction()
{
char szIPAddress[ GetMaxIPV4StringLength() ];
SomeIPGetFunction( szIPAddress );
}
static inline constexpr const auto
可能更好。 - JiaHao Xuconstexpr
意味着 const
和 inline
;static
不被隐含,因此添加它会改变可见性。 - ShadowRanger以前元编程有一个模式:
template<unsigned T>
struct Fact {
enum Enum {
VALUE = Fact<T-1>*T;
};
};
template<>
struct Fact<1u> {
enum Enum {
VALUE = 1;
};
};
// Fact<10>::VALUE is known be a compile-time constant
我相信,constexpr
的引入使您能够编写此类构造,而无需使用模板和特化、SFINAE 等奇怪的构造,但与您编写运行时函数完全相同,只是保证结果将在编译时决定。
但是请注意:
int fact(unsigned n) {
if (n==1) return 1;
return fact(n-1)*n;
}
int main() {
return fact(10);
}
使用 g++ -O3
编译此代码,您将看到 fact(10)
确实在编译时被计算!
一个支持VLA的编译器(即以C99模式运行的C编译器或具有C99扩展的C++编译器)甚至可以让您执行以下操作:
int main() {
int tab[fact(10)];
int tab2[std::max(20,30)];
}
但目前它并不是标准的C++ - constexpr
似乎是解决这个问题的一种方式(即使在没有VLA的情况下,也是如此)。而且还存在需要将“正式”的常量表达式作为模板参数的问题。
std::array<int, fact(2)>
,你会发现fact()没有在编译时评估。这只是GCC优化器做得很好。 - user283145#include <iostream>
#include "seephit.h"
using namespace std;
int main()
{
constexpr auto parser =
R"*(
<span >
<p color="red" height='10' >{{name}} is a {{profession}} in {{city}}</p >
</span>
)*"_html;
spt::tree spt_tree(parser);
spt::template_dict dct;
dct["name"] = "Mary";
dct["profession"] = "doctor";
dct["city"] = "London";
spt_tree.root.render(cerr, dct);
cerr << endl;
dct["city"] = "New York";
dct["name"] = "John";
dct["profession"] = "janitor";
spt_tree.root.render(cerr, dct);
cerr << endl;
}
constexpr
的递归函数吗?如果可以的话,我可以看到一个用法。 - ereOn