在我看来,拥有一个“总是返回5的函数”破坏或稀释了“调用函数”的意义。必须有一个原因,或者需要这个功能,否则它就不会出现在c++ 11中。为什么会在那里?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

在我看来,如果我写一个函数,返回一个字面值,然后我进行代码检查,有人会告诉我,我应该声明一个常量值,而不是返回5。


当前回答

假设它做了一些更复杂的事情。

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

其他回答

何时使用constexpr:

只要有编译时间常数。

你的基本例子和常数本身的例子是一样的。为什么要使用

static const int x = 5;
int arr[x];

over

int arr[5];

因为这样更容易维护。使用constexpr写和读要比现有的元编程技术快得多。

简介

引入Constexpr并不是为了告诉实现可以在需要常量表达式的上下文中求值;一致性实现在c++ 11之前就已经证明了这一点。

实现无法证明的是某段代码的意图:

开发人员想用这个实体表达什么? 我们是否应该盲目地允许在常量表达式中使用代码,仅仅因为它碰巧有效?


没有警察,这个世界将会怎样?

假设您正在开发一个库,并意识到您希望能够计算区间(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——它从未打算在常量表达式中使用;即使是这样,我们也依赖编译器来诊断这样的上下文(因为它忽略了我们的意图)。

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;

例如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;
}