Question

The following program compiles:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage

int main()
{
    Test<&var> test;
}

This one, however, doesn't, which is a surprise for me:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
    Test<ptr> test; //FAIL! Expected constant expression.
}

Alternative example:

int main()
{
   const int size = 42;
   int ok[*&size]; //OK

   const int * const pSize = &size;
   int fail[*pSize]; //FAIL
}

I have concluded that a pointer just can't be a constant expression regardless of whether it's const and initialized with a constant expression.

Questions:

  1. Is my conclusion true?
  2. If so, why can't a pointer be a constant expression? If not, why don't the above programs compile?
  3. Does C++0x(C++11, if you will) change anything?

Thanks for any insights!

Was it helpful?

Solution

It's a bit more complicated. In C++03 and C++11, &var is a constant expression if var is a local static / class static or namespace scope variable. This is called an address constant expression. Initializing a class static or namespace scope pointer variable with that constant expression is guaranteed to be done before any code is run (static initialization phase), because of it being a constant expression.

However only since C++11, a constexpr pointer variable that stores the address &var can also be used as an address constant expression and only since C++11, you can dereference an address constant expression (actually, you can dereference even more - even local array element addresses, but let's keep it ontopic) and if it refers to a constant integral variable initialized prior to the dereference or a constexpr variable, you again get a constant expression (depending on the type and value category, the kind of constant expression may vary). As such, the following is valid C++11:

int const x = 42;
constexpr int const *px = &x;

// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }

If so, why can't a pointer be a constant expression? If not, why don't the above programs compile?

This is a known limitation in the Standard's wording - it currently only allows other template parameters as arguments or & object, for a template parameter of pointer type. Even though the compiler should be capable of doing much more.

OTHER TIPS

It's still not allowed in C++0x. temp.arg.nontype requires:

A template-argument for a non-type, non-template template-parameter shall be one of:

  • for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
  • the name of a non-type template-parameter; or
  • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
  • a constant expression that evaluates to a null pointer value (4.10); or
  • a constant expression that evaluates to a null member pointer value (4.11); or
  • a pointer to member expressed as described in 5.3.1.

original answer:

  1. In C++03, only integral expressions can be constant expressions.
  2. Because the standard says so (naturally).
  3. In C++0x, n3290 includes examples using constexpr on a pointer. So what you are trying should now be possible, although you must now use the constexpr keyword instead of top-level const.

There's also a gcc bug involved, g++ rejects the standard draft's own examples of valid constexpr usage.

The problem is because your C++ program can be loaded at any point in memory, and so the address of a global var may be different each time you run the program. What happens if you run your program twice? var is obviously in two different locations then.

Even worse, in your example, you take the address of a variable on the stack! look at this:

void myfunction( unsigned int depth) {
     const int myvar = depth;
     const int * const myptr = &myvar;
     if (depth)
         myfunction(depth-1);
}

If main calls myfunction(3), then 3 myvars are created at seperate locations. There's no way for the compile time to even know how many myvars are created, much less there exact locations.

Finally: declaring a variable to be const means: "I promise", and does not mean that is a compile time constant. See this example:

int main(int argc, char** argv) {
    const int cargc = argc;
    char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top