在常量表达式中调用的`static constexpr`函数是......错误?

Kyl*_*and 23 c++ constant-expression noexcept constexpr c++11

我有以下代码:

class MyClass
{
  static constexpr bool foo() { return true; }
  void bar() noexcept(foo()) { }    
};
Run Code Online (Sandbox Code Playgroud)

我希望,因为它foo()是一个static constexpr函数,并且因为它是在bar声明之前定义的,所以这是完全可以接受的.

但是,g++给我以下错误:

 error: ‘static constexpr bool MyClass::foo()’ called in a constant expression
Run Code Online (Sandbox Code Playgroud)

这是......不太有用,因为在常量表达式中调用函数的能力是整个点constexpr.

clang++更有帮助.除了声明参数noexcept必须是常量表达式的错误消息之外,它还说:

note: undefined function 'foo' cannot be used in a constant expression
note: declared here
static constexpr bool foo() { return true; }
                      ^
Run Code Online (Sandbox Code Playgroud)

那么......这是一个两遍编译问题吗?问题是编译器在定义任何成员函数之前是否尝试声明它们中的所有成员函数?(注意,在类的上下文之外,编译器都不会抛出错误.)这让我感到惊讶; 直观地说,我认为static constexpr成员函数不能在任何和所有常量表达式中使用,无论是在类中还是在类中.

Kyl*_*and 12

正如TC在评论中展示了一些链接,标准对此并不十分清楚; 使用跟踪返回类型会出现类似的问题decltype(memberfunction()).

核心问题是,在宣布类成员完成之前,通常不会认为是成员.因此,不管事实foostatic constexpr其声明是之前的bar,它不能被视为"可用"使用的常量表达式中,直到MyClass完成.

正如Shafik Yaghmour指出的那样,标准中有一些尝试可以避免依赖于类中成员的排序,显然允许原始问题中的示例进行编译会引入排序依赖(因为foo需要声明)之前bar).但是,对排序已经有一点依赖,因为虽然constexpr函数不能在内部调用noexcept,但noexcept表达式本身可能依赖于类中的早期声明:

class MyClass
{
    // void bar() noexcept(noexcept(foo())); // ERROR if declared here
    static constexpr bool foo();
    void bar() noexcept(noexcept(foo())); // NO ERROR
}
Run Code Online (Sandbox Code Playgroud)

(请注意,这实际上并不违反3.3.7,因为这里仍然只有一个正确的程序.)

这种行为实际上可能违反了标准; TC指出(在下面的评论中)foo这里应该实际上在整个班级的范围内查找.当bar首次声明时,g ++ 4.9.2和clang ++ 3.5.1都会失败并出现错误,但在首次声明时编译时没有错误或警告foo.编辑: clang ++ trunk-revision 238946(从3.7.0发布之前不久)在第一次声明时不会失败bar; g ++ 5.1仍然失败.

有趣的是,以下变体会导致使用clang ++的"异常说明符",但不会导致g ++:

class MyClass
{
  static constexpr bool foo2();
  void bar2() noexcept(noexcept(foo2()));
};

constexpr bool MyClass::foo2() { return true; }
void MyClass::bar2() noexcept(noexcept(MyClass::foo2())) { }
Run Code Online (Sandbox Code Playgroud)

根据该错误,noexcept对于本说明书中声明bar2计算结果为noexcept(false),然后将其认为是不匹配noexcept(noexcept(MyClasss::foo2())).

  • 标准说你可以做`struct C {int*x =&y; int y; 当然``struct C {int x(){return y; } int y; 在这两种情况下,您都可以在声明之前使用类成员,因此显然必须将至少部分成员声明的解析延迟到类完成. (4认同)