【问题标题】:constexpr not working if the function is declared inside class scope如果函数在类范围内声明,则 constexpr 不起作用
【发布时间】:2013-05-05 19:27:13
【问题描述】:

我使用的是 g++4.8.0,它不包含早期的 constexpr 错误。因此下面的代码工作fine

constexpr int size() { return 5; }
int array[size()];

int main () {}

但是,如果我将两个变量都包含在 class 中作为 static,那么它会给出 compiler error

struct X {
  constexpr static int size() { return 5; }
  static const int array[size()]; 
};

int main () {}

这是错误:

错误:数组“array”的大小不是一个整数常量表达式

是否禁止以这种方式使用constexpr 或另一个 g++ 错误?

【问题讨论】:

  • 我在浏览标准后没有发现任何不允许的内容。
  • 由于成员函数定义可以引用尚未声明的成员,如果这是合法的,你也可以这样做constexpr static int size() { return sizeof(array)/sizeof(*array) + 1; }...
  • 这是CWG1255(最近是CWG2335)。

标签: c++ c++11 compiler-errors g++ constexpr


【解决方案1】:

是的,它的格式不正确。原因如下:

constexpr 函数在用于常量表达式之前需要定义(而不仅仅是声明)。

例如:

constexpr int f(); // declare f
constexpr int x = f(); // use f - ILLEGAL, f not defined
constexpr int f() { return 5; } // define f, too late

类说明符内的函数定义(以及初始化程序和默认参数)本质上是按照它们在类外定义的顺序进行解析的。

所以这个:

struct X {
  constexpr static int size() { return 5; }
  static const int array[size()]; 
};

按此顺序解析:

struct X {
   constexpr inline static int size(); // function body defered
   static const int array[size()];  // <--- POINT A
};

constexpr inline int X::size() { return 5; }

也就是说,函数体的解析被推迟到类说明符之后。

延迟函数体解析的目的是为了让函数体可以转发当时尚未声明的引用类成员,并且它们可以将自己的类用作完整类型:

struct X
{
    void f() { T t; /* OK */ }
    typedef int T;
};

与在命名空间范围内相比:

void f() { T t; /* error, T not declared */ }
typedef int T;

POINT A,编译器还没有size()的定义,所以不能调用。对于编译时性能,constexpr 函数需要在编译期间调用之前在翻译单元中使用之前进行定义,否则编译器必须进行多次传递才能“链接”常量表达式以进行评估。

【讨论】:

  • 您或多或少是正确的。正式的答案是内联编写的成员函数的定义点位于类定义的末尾。除了我认为标准从未真正出来并这么说。
  • @BenVoigt:实现该要求的唯一方法是将函数体的解析推迟到类说明符之后。您需要名称查找才能解析 C++。所以基本上你分隔函数波特,默认参数和初始化器 - 解析类说明符 - 然后解析以前延迟的分隔项。
  • 这仍然是一个实现细节。实际的规则是类在它自己的定义中是不完整的。但是应该有一条规则禁止在不完整的类型上调用静态成员函数(同时允许访问先前声明的静态数据成员),我找不到。
  • +1 以获得详细解释,但我相信这更多是标准的限制。例如,如果全局函数在使用前声明并在文件末尾定义,在这种情况下编译器会给出相同的错误。为什么编译器也不能对 static 成员应用相同的策略。如果它的定义如我的代码中所述,则允许使用它,否则会出错。
  • @iammilind:因为在类说明符内内联定义的函数需要能够使用尚未在该类说明符中声明的内容。这在命名空间范围内是不一样的。在命名空间范围内,函数体被解析为函数定义的一部分,正如您所期望的那样。这不是标准的限制,它是 C++“单通道”编译设计的一个不可避免的属性。
【解决方案2】:

显然是it's not even a bug,因为它的状态是RESOLVED INVALID,也就是说GCC和那个bugzilla背后的人,在审查了问题之后,不要认为这是一个GCC的bug。

我在该页面上提醒您,因为在其中一个相关帖子中也有此行为的答案。

【讨论】:

  • @chris 不是那样的,标准说明了应该如何编写该结构以及为什么这会给你一个错误。
  • +1 用于查找确切的示例链接。然而,他们所讨论的似乎并不合理。如果成员方法可以在外部声明和定义。全局函数也可以在以后声明和定义。如果编译器可以将后一种情况区分为“何时给出错误,何时不给出错误”,那么为什么不区分前一种情况呢?
  • @iammilind:要在常量表达式中使用全局 constexpr 函数,必须在其在翻译单元中的使用之前对其进行定义(而不仅仅是声明)。
  • 我认为这是标准中的一个缺陷。它应该直截了当地说,直到类定义结束时才定义类成员函数。相反,我们有一条规则说类在其自己的定义中是不完整的,但这并不能解释为什么可以使用静态constexpr 成员函数而可以使用静态constexpr 数据成员。
  • @BenVoigt:当然是这么说的。请参阅 3.3.7 类范围。
【解决方案3】:

我只是想补充一点,尽管这可能不是一个好的做法,并且会限制您在其声明的同一编译单元中定义类体,但可以强制编译器在以下位置编译函数体的定义通过添加冗余模板参数与声明相同点:

template <typename = void>
struct X {
   constexpr static int size() { return 5; }
   static const int array[size()]; 
};

int main()
{
    X<> x{};
    ...
}

【讨论】:

    猜你喜欢
    • 2016-02-25
    • 2011-06-12
    • 2015-04-12
    • 2015-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多