为什么常量指针不能成为常量表达式?

时间:2011-09-12 18:15:00

标签: c++ pointers c++11 constant-expression

以下程序编译:

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 个答案:

答案 0 :(得分:12)

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

但是只有自C ++ 11以来,存储地址&var constexpr 指针变量也可以用作地址常量表达式,并且只有从C ++ 11开始,你才可以取消引用地址常量表达式(实际上,您可以更多地取消引用 - 甚至是局部数组元素地址,但让它保持ontopic)并且如果它引用在取消引用之前初始化的常量积分变量或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,用于指针类型的模板参数。即使编译器应该能够做更多的事情。

答案 1 :(得分:2)

在C ++ 0x中仍然不允许这样做。 temp.arg.nontype要求:

  

非类型非模板模板参数的模板参数应为以下之一:

     
      
  • 表示整数或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);或
  •   
  • 非类型模板参数的名称;或
  •   
  • 一个常量表达式(5.19),用于指定具有静态存储持续时间的对象的地址   外部或内部链接或具有外部或内部链接的功能,包括功能模板   和函数template-ids但不包括非静态类成员,表示(忽略括号)为   & id-expression ,但&amp;如果名称引用函数或数组,则可以省略   如果相应的模板参数是引用,则省略;或
  •   
  • 一个求值为空指针值的常量表达式(4.10);或
  •   
  • 一个常量表达式,其值为null成员指针值(4.11);或
  •   
  • 指向成员的指针,如5.3.1所述。
  •   

原始答案:

  1. 在C ++ 03中,只有整数表达式可以是常量表达式。
  2. 因为标准这样说(自然)。
  3. 在C ++ 0x中,n3290包含在指针上使用constexpr的示例。因此,您现在应该可以尝试,但现在必须使用constexpr关键字而不是顶级const
  4. 还有一个gcc错误,g++ rejects the standard draft's own examples of valid constexpr usage

答案 2 :(得分: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个myvars。编译时甚至无法知道如何创建许多 myvars,更不用说精确的位置了。

最后:将变量声明为const意味着:&#34;我保证&#34;,并且意味着它是编译时常量。见这个例子:

int main(int argc, char** argv) {
    const int cargc = argc;
    char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}