質問

I seem to be missing something rather fundamental. I'm trying to use const array members at compile-time.

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

Errors:

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

Here is the relevent IDEOne link

So why doesn't this work? What am I missing? What should I do differently?

役に立ちましたか?

解決

Just because an object is const doesn't mean it's a compile time constant expression.

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 };
          ^

This is the reason for 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

As for why this works:

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

initializing a const variable doesn't require a constant expression:

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

他のヒント

Expressions are not constant expressions if they contain any one of a number of disallowed sub-expressions. One such class of disallowed sub-expressions is:

  • an lvalue-to-rvalue conversion (4.1) unless it is applied to
    • a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or
    • a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or
    • a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant expression;

In particular, while the name of a const object of enum or intergral type initialized with a constant initializer forms a constant expression (reading its value is what causes the lvalue-to-rvalue conversion), sub-objects of an const aggregate object (such as list in your example, an array) do not, but would if declared constexpr.

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

This is valid but a does not constitute a constant expression because it is not initialized with a constant expression.

By changing the declaration of list (we don't have to change the declaration of a), we can make a form a constant expression.

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

As list[2] is now a constant expression, a is now a const object of intergral type initialized with a constant expression so a can now be used as a constant expression.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top