问题 编译时数组常量


我似乎错过了一些相当基本的东西。 我正在尝试在编译时使用const数组成员。

const int list[3] = { 2, 5, 7 };
const int a = list[2]; // this doesn't error?

template<int N1, int N2>
struct tmax {
  enum { value = ((N1 > N2) ? N1 : N2) };
};

const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // error is here

int main()
{
  return 0;
}

错误:

prog.cpp:10:24: error: 'list' cannot appear in a constant-expression
prog.cpp:10:30: error: an array reference cannot appear in a constant-expression

这里是 相关的IDEOne链接

那么为什么这不起作用呢?我错过了什么?我该怎么办?


9301
2018-06-09 23:10


起源



答案:


仅仅因为一个对象 const 并不意味着它是一个编译时常量表达式。

main.cpp:10:20: error: non-type template argument is not a constant expression
const int c = tmax<list[0],list[1]>::value; // error is here
                   ^~~~~~~
main.cpp:10:20: note: read of non-constexpr variable 'list' is not allowed in a constant expression
main.cpp:1:11: note: declared here
const int list[3] = { 2, 5, 7 };
          ^

这就是原因 constexpr

constexpr int list[3] = { 2, 5, 7 };

template<int N1, int N2>
struct tmax {
    enum { value = ((N1 > N2) ? N1 : N2) };
};

const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // works fine now

至于为什么这样做:

const int a = list[2]; // this doesn't error?

初始化a const 变量不需要常量表达式:

int foo(int n) {
    const int a = n; // initializing const var with a non-compile time constant

9
2018-06-09 23:18



我认为constexpr会有所帮助,现在我觉得它看起来比我想象的更广泛。有了这个说,关于为什么const int a的声明工作但是列表[1]的具体做法的一点解释不会被理解。谢谢。 - std''OrgnlDave
好的,因此visual studio 2012不打算支持constexpr。有人有任何其他解决方案?我不必为此使用它,但它会很好。 - std''OrgnlDave
@ std''OrgnlDave我添加了更多的编译器诊断,它解释了为什么list [1]不是常量表达式。 - bames53


答案:


仅仅因为一个对象 const 并不意味着它是一个编译时常量表达式。

main.cpp:10:20: error: non-type template argument is not a constant expression
const int c = tmax<list[0],list[1]>::value; // error is here
                   ^~~~~~~
main.cpp:10:20: note: read of non-constexpr variable 'list' is not allowed in a constant expression
main.cpp:1:11: note: declared here
const int list[3] = { 2, 5, 7 };
          ^

这就是原因 constexpr

constexpr int list[3] = { 2, 5, 7 };

template<int N1, int N2>
struct tmax {
    enum { value = ((N1 > N2) ? N1 : N2) };
};

const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // works fine now

至于为什么这样做:

const int a = list[2]; // this doesn't error?

初始化a const 变量不需要常量表达式:

int foo(int n) {
    const int a = n; // initializing const var with a non-compile time constant

9
2018-06-09 23:18



我认为constexpr会有所帮助,现在我觉得它看起来比我想象的更广泛。有了这个说,关于为什么const int a的声明工作但是列表[1]的具体做法的一点解释不会被理解。谢谢。 - std''OrgnlDave
好的,因此visual studio 2012不打算支持constexpr。有人有任何其他解决方案?我不必为此使用它,但它会很好。 - std''OrgnlDave
@ std''OrgnlDave我添加了更多的编译器诊断,它解释了为什么list [1]不是常量表达式。 - bames53


表达式不是 常数表达式 如果它们包含许多不允许的子表达式中的任何一个。一类这样的不允许的子表达式是:

  • 除非适用,否则左值到右值的转换(4.1)
    • 一个整数或枚举类型的glvalue,它引用一个带有前面的非易失性const对象   初始化,用常量表达式初始化,或
    • 一个文字类型的glvalue,它引用一个用它定义的非易失性对象 constexpr或者指的是   到这样一个对象的子对象,或
    • 文字类型的glvalue,指的是一生中没有的非易失性临时对象   结束,用一个常量表达式初始化;

特别是,使用常量初始化程序初始化的枚举或整数类型的const对象的名称形成一个 不断表达 (读取它的值是导致左值到右值转换的原因),const聚合对象的子对象(例如 list 在你的例子中,一个数组)没有,但如果声明 constexpr

const int list[3] = { 2, 5, 7 };
const int a = list[2];

这是有效的但是 a 不构成一个 不断表达 因为它没有用a初始化 不断表达

通过改变声明 list (我们不需要改变声明 a), 我们可以做 a 形成一个恒定的表达。

constexpr int list[3] = { 2, 5, 7 };
const int a = list[2];

list[2] 现在是 不断表达a 现在是 const 用a初始化的整数类型的对象 不断表达 所以 a 现在可以用作常量表达式。


4
2018-06-09 23:35



它几乎让我哭泣,视觉工作室2k12不会支持constexpr。我从来没有在模板元编程中使用这样的数组,但我很期待它...... - std''OrgnlDave