C ++:为什么这个constexpr不是编译时常量

时间:2015-01-22 01:20:48

标签: templates c++11 constexpr

在下面的C ++ 11代码中,对arraySize的最后一次调用会导致编译错误。显然这是因为y是运行时大小的数组,并且不能推导出y的arraySize模板参数N.我不明白为什么x是一个编译时大小的数组,但y结束了运行时大小。 arraySize模板函数直接取自Scott Meyers的“Effective Modern C ++”第1项。

#include <cstddef>

template<typename T, std::size_t N>
constexpr std::size_t arraySize(T(&)[N]) noexcept { return N; }

struct S
{
    char c[10];
};

int main()
{
    S s;
    S* ps = &s;

    char x[arraySize(s.c)];
    char y[arraySize(ps->c)]; // why is y a runtime sized array?

    arraySize(x);
    arraySize(y); // error !?

    return 0;
}

1 个答案:

答案 0 :(得分:9)

在C ++中,错误不是对arraySize(y)的调用,而是y本身的声明。

数组声明中的边界必须是&#34;转换的常量表达式&#34;。

如果您的编译器接受y的声明,后来告诉您y是运行时绑定的数组,则它不是C ++编译器。在任何已批准的C ++版本中都没有绑定运行时数组,也没有当前的草案。

arraySize(s.c)arraySize(ps->c)之间的显着差异是ps->c(*ps).c相同,而*解引用运算符需要左值到右值转换在ps上,这不是一个常量表达式(也不是&s,见下文)。表达式的其余部分不涉及左值到右值的转换,数组左值直接由引用绑定。

  

常量表达式是glvalue核心常量表达式,其值指的是一个实体,它是常量表达式的允许结果(如下定义),或者是一个prvalue核心常量表达式,其值是一个对象,其中,对于该对象及其子对象:

     
      
  • 引用类型的每个非静态数据成员指的是一个常量的允许结果的实体   表达式,

  •   
  • 如果对象或子对象是指针类型,则它包含具有静态存储持续时间的对象的地址,超过此类对象末尾的地址(5.7),函数的地址,或空指针值。

  •   
     

实体是常量表达式的允许结果,如果它是具有静态存储持续时间的对象,该对象不是临时对象,或者是其值满足上述约束的临时对象,或者它是   功能

显然ps包含具有自动存储持续时间的对象的地址,因此无法声明constexpr。但是,如果您将S s; S* ps = &s;更改为static S s; constexpr S* ps = &s;

,一切都应该开始有效

(另一方面,您认为arraySize(s.c)的参数也不是常量表达式,因为它是一个引用,而不是静态存储持续时间的对象)