const vs constexpr关于变量

fre*_*low 275 c++ variables const constexpr c++11

以下定义之间有区别吗?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Run Code Online (Sandbox Code Playgroud)

如果没有,在C++ 11中首选哪种风格?

How*_*ant 316

我相信存在差异.让我们重命名它们,以便我们可以更容易地讨论它们:

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Run Code Online (Sandbox Code Playgroud)

这两个PI1PI2是不变的,这意味着你不能修改它们.但是只有 PI2编译时常量.它在编译时初始化. PI1可以在编译时或运行时初始化.此外,只能 PI2在需要编译时常量的上下文中使用.例如:

constexpr double PI3 = PI1;  // error
Run Code Online (Sandbox Code Playgroud)

但:

constexpr double PI3 = PI2;  // ok
Run Code Online (Sandbox Code Playgroud)

和:

static_assert(PI1 == 3.141592653589793, "");  // error
Run Code Online (Sandbox Code Playgroud)

但:

static_assert(PI2 == 3.141592653589793, "");  // ok
Run Code Online (Sandbox Code Playgroud)

至于你应该使用哪个?使用符合您需求的任何一种.您是否希望确保您具有可在需要编译时常量的上下文中使用的编译时常量?您是否希望能够在运行时进行计算来初始化它?等等.

  • 你确定吗?因为`const int N = 10; char a [N];`工作,数组边界必须是编译时常量. (56认同)
  • @FredOverflow:非const数组索引已经"工作"了大约十年(例如g ++扩展),但这并不意味着它是严格合法的C++(虽然一些更新的C或C++标准_使它合法_,我忘了哪一个).至于编译时常量的差异,模板参数和用作`enum`初始化程序是`const`和`constexpr`之间唯一的两个值得注意的区别(并且无论如何都不适用于`double`). (29认同)
  • 5.19常量表达式[expr.const]的第4段也是一个(非规范性)注释,着名的是,允许实现在编译时以不同于运行时的方式进行浮点运算(例如,关于精度).所以`1/PI1`和`1/PI2`可能产生不同的结果.我认为这种技术性并不像本答案中的建议那么重要. (16认同)
  • 我确信,就我写的例子而言(在发布前测试每一个例子).但是我的编译器确实允许我将`PI1`转换为编译时整数常量以用于数组,但不能用作非类型的整数模板参数.所以`PI1`的编译时可转换性对于一个整数类型似乎有点受到打击并且想念我. (9认同)
  • 但它'constexpr double PI3 = PI1;`对我来说正常.(MSVS2013 CTP).我究竟做错了什么? (4认同)
  • @ user2198121:你没有做错任何事.只知道你正在利用编译器扩展.代码是安全的,但不可移植.在移植时,将在编译时找到任何差异,而不是运行时. (2认同)
  • `PI2` 将具有内部链接,这意味着它对于翻译单元来说是本地的。理论上它会有一个地址。但是,如果该地址从未使用过,则它不需要存在。如果 PI2 曾经通过引用传递,编译器将需要将它存储在某个地方以供引用引用(除非该函数被内联并且编译器能够优化掉引用)。 (2认同)
  • 给任何对 constexpr double PI3 = PI1; 感到困惑的人的注释 // error` - 该错误是编译器特定的。例如,一些编译器会继续看到“PI1”本身是一个文字,因此确定这是合法的。其他人,由于“const”的性质,不会执行此检查,因为它可能是也可能不是文字。 (2认同)
  • @yapkm01:可能只是编译器错误。这里是试验各种编译器的好地方:http://melpon.org/wandbox/permlink/4blONXNuKtzFkpMO (2认同)

Pet*_*ker 72

这里没有区别,但是当你有一个具有构造函数的类型时,这很重要.

struct S {
    constexpr S(int);
};

const S s0(0);
constexpr S s1(1);
Run Code Online (Sandbox Code Playgroud)

s0是一个常量,但它不承诺在编译时初始化.s1是标记的constexpr,所以它是一个常量,因为它S的构造函数也被标记constexpr,它将在编译时初始化.

大多数情况下,这很重要,因为在运行时初始化会非常耗时,并且您希望将该工作推送到编译器上,这也很耗时,但不会减慢编译程序的执行时间

  • @MatthieuM:我怀疑`foo(s0)`是否会在编译时执行,但你永远不会知道:允许编译器进行这样的优化.当然,gcc 4.7.2和clang 3.2都不允许我编译`constexpr a = foo(s0);` (4认同)
  • 我同意:我得出的结论是,如果对象的编译时计算不可能,那么`constexpr`将导致诊断.不太清楚的是,如果参数被声明为`const`而不是`constexpr`,那么在编译时是否可以执行*期望*常量参数的函数:即,将执行`constexpr int foo(S)`在编译时如果我调用`foo(s0)`? (3认同)

Aja*_*dav 40

constexpr表示编译期间常量且已知的值.
const表示一个仅为常数的值; 在编译期间不必知道.

int sz;
constexpr auto arraySize1 = sz;    // error! sz's value unknown at compilation
std::array<int, sz> data1;         // error! same problem

constexpr auto arraySize2 = 10;    // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr
Run Code Online (Sandbox Code Playgroud)

请注意,const不提供与constexpr相同的保证,因为const对象无需使用编译期间已知的值进行初始化.

int sz;
const auto arraySize = sz;       // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation
Run Code Online (Sandbox Code Playgroud)

所有constexpr对象都是const,但并非所有const对象都是constexpr.

如果您希望编译器保证变量的值可以在需要编译时常量的上下文中使用,那么要实现的工具是constexpr,而不是const.

  • 我很喜欢你的解释lot..can请您多加评论上哪里有情况下,我们可能需要使用在现实生活中的情景编译时间常数. (2认同)

Jna*_*ana 16

constexpr符号常数,所以必须考虑到在编译时已知的值.例如:

?constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    constexpr int c2 = n+7;   // Error: we don’t know the value of c2
    // ...
}
Run Code Online (Sandbox Code Playgroud)

为了处理使用在编译时未知但在初始化后永远不会更改的值初始化的"变量"的值的情况,C++提供了第二种形式的常量(const).例如:

?constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    const int c2 = n+7; // OK, but don’t try to change the value of c2
    // ...
    c2 = 7; // error: c2 is a const
}
Run Code Online (Sandbox Code Playgroud)

这种" 常量变量"非常常见,原因有两个:

  1. C++ 98没有constexpr,所以人们使用const.
  2. 列表项"变量"不是常量表达式(它们的值在编译时是未知的)但在初始化后不更改值本身就非常有用.

参考:Stroustrup的"编程:使用C++的原理和实践"

  • 也许你应该提到你的答案中的文字是从Stroustrup的"编程:使用C++的原理和实践"中逐字逐句的. (25认同)

Sri*_*tha 5

const再举一个例子来理解和之间的区别constexp

int main()
{
    int n;
    cin >> n;               

    const int c = n;        // OK: 'c' can also be initialized at run time
    constexpr int e = n;    // Error: 'e' must be initialized at compile time
}
Run Code Online (Sandbox Code Playgroud)

注意: constexpr 通常在编译时求值,但不保证它们会这样做,除非在需要常量表达式的上下文中调用它们。

constexpr int add(int a, int b)
{
    return a + b;
};


int main()
{
    int n = add(4, 3);          // may or may not be computed at compile time
    constexpr int m = add(4,3); // must be computed at compile time
}
Run Code Online (Sandbox Code Playgroud)