我有以下代码:
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
成员函数在任何和所有常量表达式中都无法使用的理由,无论是在课堂内还是外部。
答案 0 :(得分:12)
作为T.C.通过评论中的一些链接进行了演示,该标准并非完全清楚;使用decltype(memberfunction())
跟踪返回类型会产生类似的问题。
核心问题是,类成员通常不被认为是在声明它们被完成的类之后才被声明。因此,无论foo
是static constexpr
并且其声明都在bar
之前的事实,在MyClass
之前,它不能被视为在“常量表达式”中使用“可用”完整。
作为pointed out by Shafik Yaghmour,标准中有一些尝试可以避免依赖于类中成员的排序,显然允许原始问题中的示例编译会引入排序依赖(从{{{ 1}}需要在foo
之前声明。但是,已经依赖于排序,因为尽管bar
函数在constexpr
内无法调用,但noexcept
} expression 本身可能依赖于类中的早期声明:
noexcept
(请注意,这实际上并不违反3.3.7,因为此处仍然只有一个正确的程序。)
这种行为实际上可能违反了标准; T.C。指出(在下面的评论中)这里class MyClass
{
// void bar() noexcept(noexcept(foo())); // ERROR if declared here
static constexpr bool foo();
void bar() noexcept(noexcept(foo())); // NO ERROR
}
实际上应该在整个班级的范围内查找。当首先声明foo
时,g ++ 4.9.2和clang ++ 3.5.1都会失败,但在首先声明bar
时编译没有错误或警告。 编辑: clang ++ trunk-revision 238946(从3.7.0发布之前不久)在首先声明foo
时不失败; g ++ 5.1仍然失败。
有趣的是,以下变体会导致使用clang ++的“异常说明符”,但不使用g ++:
bar
根据错误,class MyClass
{
static constexpr bool foo2();
void bar2() noexcept(noexcept(foo2()));
};
constexpr bool MyClass::foo2() { return true; }
void MyClass::bar2() noexcept(noexcept(MyClass::foo2())) { }
的声明的noexcept
规范评估为bar2
,这被认为是{{1}的不匹配}。