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

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

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

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


当前回答

例如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函数真的很好,是对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。

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

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

over

int arr[5];

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

另一个用途(尚未提及)是constexpr构造函数。这允许创建编译时常量,这些常量不必在运行时进行初始化。

const std::complex<double> meaning_of_imagination(0, 42); 

将其与用户定义的字面量配对,就可以完全支持字面的用户定义类。

3.14D + 42_i;

曾经有一种元编程模式:

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编译它,你会看到事实(10)确实在编译时被求值了!

一个VLA-aware编译器(C99模式下的C编译器或带有C99扩展的c++编译器)甚至可以允许你做:

int main() {
    int tab[fact(10)];
    int tab2[std::max(20,30)];
}

但目前它是非标准的c++ - constexpr看起来是一种解决这个问题的方法(即使没有VLA,在上面的例子中)。还有一个问题,就是需要有“正式的”常量表达式作为模板参数。