在我看来,拥有一个“总是返回5的函数”破坏或稀释了“调用函数”的意义。必须有一个原因,或者需要这个功能,否则它就不会出现在c++ 11中。为什么会在那里?
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
在我看来,如果我写一个函数,返回一个字面值,然后我进行代码检查,有人会告诉我,我应该声明一个常量值,而不是返回5。
Constexpr函数真的很好,是对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;
这里的许多回答似乎有些相反,或者把安静的部分大声说出来,把吵闹的部分小声说出来。关于constexpr你需要知道的一件关键的事情是:
// This guarantees only that the value of "MeaningOfLife" can not be changed
// from the value calculated on this line by "complex_initialization()"
// (unless you cast away the const of course, don't do that).
// Critically here, everything happens at *runtime*.
const int MeaningOfLife = complex_initialization(1234, 5678, "hello");
// This guarantees that "MeaningOfLife" is fully evaluated and "initialized"
// *at compile time*. If that is not possible due to complex_initialization()
// not being evaluatable at compile time, the compiler is required to abort
// compilation of the program.
// Critically here, to put a fine point on it, everything happens at
// *compile time*, guaranteed. There won't be a runtime call to
// complex_initialization() at all in the final program.
constexpr int MeaningOfLife = complex_initialization(1234, 5678, "hello");
注意,是左边的常量使保证有存在的理由。当然,这取决于你是否能确保右边的值在编译时被求出来,重要的是,仅仅声明一个函数constexpr本身并不能做到这一点。
因此,您的问题的答案是,当您需要或希望它的初始化(右边发生的所有事情)完全在编译时发生或中断构建时,您应该声明一个变量constexpr。
假设它做了一些更复杂的事情。
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 (Pi)弧度,但下面写得更清楚:
const float oneeighty = DegreesToRadians( 180.0f );
这里有很多好的信息:
http://en.cppreference.com/w/cpp/language/constexpr
Stroustrup在“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