【问题标题】:`static constexpr` function called in a constant expression is...an error?常量表达式中调用的`static constexpr`函数是...错误?
【发布时间】:2015-06-15 13:40:45
【问题描述】:

我有以下代码:

class MyClass
{
  static constexpr bool foo() { return true; }
  void bar() noexcept(foo()) { }    
};

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

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

 error: ‘static constexpr bool MyClass::foo()’ called in a constant expression

这...没什么用,因为在常量表达式中调用函数的能力是constexpr的全部要点。

clang++ 更有帮助。除了指出noexcept 的参数必须是常量表达式的错误消息外,它还显示:

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

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

【问题讨论】:

  • @MattMcNabb 但是bar 的主体在您的示例中使用它的noexcept 表达式之前没有完整,因此没有不一致。
  • @T.C.我对你找到这些宝石的能力感到困惑。你应该写一个答案,因为这是非常不明显的。
  • 来自 T.C. 的链接假设标准说在类声明完成之前不能引用 constexpr (})。
  • @KyleStrand 也许我的例子不是很好。但目前不依赖于定义了哪些订单函数;或者它们在类中声明的顺序。您的提议将介绍这一点,这可能是一罐蠕虫。 CWG 1255 中的 auto 示例说明了其中一个问题。

标签: c++ c++11 constexpr noexcept constant-expression


【解决方案1】:

作为 T.C.通过评论中的一些链接进行演示,标准对此并不相当清楚;使用decltype(memberfunction()) 的尾随返回类型也会出现类似的问题。

核心问题是,在声明它们的类完成之前,通常不会将类成员视为已声明。因此,不管foostatic constexpr 并且它的声明先于bar 的事实,在MyClass 完成之前,它都不能被认为是“可用的”用于常量表达式。

作为pointed out by 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
}

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

这种行为实际上可能是违反标准的; T.C.指出(在下面的评论中)这里的foo 实际上应该在整个班级的范围内查找。 g++ 4.9.2 和 clang++ 3.5.1 在首先声明 bar 时都失败并出现错误,但在首先声明 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())) { }

根据错误,bar2声明noexcept 规范评估为 noexcept(false),然后将其视为与 noexcept(noexcept(MyClasss::foo2())) 不匹配。

【讨论】:

  • 这有点令人惊讶。 exception-specification 中的名称应该在整个类的范围内查找([basic.lookup.unqual]/p8、[basic.scope.class]/p1)。我想知道这是否只是有争议的编译器不合格。
  • @T.C. ....嗯。所以你是说上面的代码 sn-p 无论声明顺序如何都应该是有效的?那不需要某种两遍声明方案吗?
  • 你已经有了成员函数体和NSDMIs;这条规则并不完全是新的。
  • @T.C.我不确定我是否立即掌握了那里的含义。你能举个例子吗?
  • 标准说你可以做struct C { int *x = &y; int y; };当然struct C { int x() { return y; } int y; }; 在这两种情况下你都可以在声明之前使用一个类成员,所以显然至少解析成员的一部分声明必须延迟到课程结束。
猜你喜欢
  • 1970-01-01
  • 2016-07-29
  • 2018-01-08
  • 1970-01-01
  • 2023-01-31
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多