Ответ 1
Как T.C. продемонстрированный с некоторыми ссылками в комментарии, стандарт не совсем ясен по этому поводу; аналогичная проблема возникает с возвратными типами возврата с помощью decltype(memberfunction())
.
Центральная проблема заключается в том, что члены класса обычно не считаются объявленными до тех пор, пока класс, в котором они объявлены, не будет завершен. Таким образом, независимо от того, что foo
является static constexpr
, и его объявление предшествует значению bar
, оно не может считаться "доступным" для использования в постоянном выражении до тех пор, пока MyClass
не будет завершено.
Как отмеченный Шафиком Ягмуром, в рамках стандарта есть некоторая попытка избежать зависимости от упорядочения членов внутри класса и, очевидно, исходный вопрос для компиляции приведет к упорядочивающей зависимости (поскольку 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, так как здесь возможно только одна правильная программа.)
Такое поведение может фактически являться нарушением стандарта; Агар Т.С. указывает (в комментарии ниже), что foo
здесь действительно нужно искать в рамках всего класса. И g++ 4.9.2, и clang++ 3.5.1 сбой при ошибке, когда bar
объявляется первым, но компилируется без ошибок или предупреждений, когда foo
объявляется первым. EDIT: 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())) { }
В соответствии с ошибкой спецификация noexcept
для объявления bar2
оценивается как noexcept(false)
, которая затем считается несоответствием для noexcept(noexcept(MyClasss::foo2()))
.