33

以下程序编译:

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

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

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

然而,这个没有,这对我来说是一个惊喜:

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

替代示例:

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

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

我得出的结论是,指针不能是常量表达式,无论它是否为 const 并用常量表达式初始化。

问题:

  1. 我的结论是真的吗?
  2. 如果是这样,为什么指针不能是常量表达式?如果没有,为什么上面的程序不能编译?
  3. C++0x(C++11,如果你愿意的话)会改变什么吗?

感谢您的任何见解!

4

3 回答 3

14

这有点复杂。在 C++03 和 C++11 中,&var如果var是局部静态/类静态或命名空间范围变量,则为常量表达式。这称为地址常量表达式。使用该常量表达式初始化类静态或命名空间范围指针变量保证在任何代码运行之前完成(静态初始化阶段),因为它是一个常量表达式。

然而,仅从 C++11 开始,存储地址的constexpr指针变量&var也可以用作地址常量表达式,并且仅从 C++11 开始,您可以取消引用地址常量表达式(实际上,您可以取消引用更多 - 甚至本地数组元素地址,但让我们保持主题),如果它引用在取消引用之前初始化的常量整数变量或 constexpr 变量,您将再次获得常量表达式(取决于类型和值类别,常量表达式的类型可能会有所不同)。因此,以下是有效的 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); }

如果是这样,为什么指针不能是常量表达式?如果没有,为什么上面的程序不能编译?

这是标准措辞中的一个已知限制 - 它目前只允许其他模板参数作为参数,或者& object,对于指针类型的模板参数。即使编译器应该能够做更多的事情。

于 2011-09-12T20:33:45.983 回答
2

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.

于 2011-09-12T18:25:20.310 回答
1

问题是因为您的 C++ 程序可以在内存中的任何位置加载,因此var每次运行程序时全局地址可能不同。如果你运行你的程序两次会发生什么? var那么显然是在两个不同的位置。

更糟糕的是,在您的示例中,您获取了堆栈上变量的地址!看这个:

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

如果 main 调用 myfunction(3),则在不同的位置创建 3 个 myvar。编译时甚至无法知道创建了多少myvar,更不用说确切的位置了。

最后:声明一个变量是const意味着:“我保证”,并不意味着这是一个编译时间常数。看这个例子:

int main(int argc, char** argv) {
    const int cargc = argc;
    char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}
于 2011-09-12T18:51:56.370 回答