Функция `static constexpr`, вызываемая в константном выражении, является... ошибкой?
У меня есть следующий код:
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
Функции-члены не должны использоваться в любых и во всех константных выражениях, внутри класса или вне.
1 ответ
Как показал TC с некоторыми ссылками в комментарии, стандарт не совсем ясен по этому поводу; аналогичная проблема возникает с конечными типами возвращаемых данных, использующими decltype(memberfunction())
,
Основная проблема заключается в том, что члены класса, как правило, не считаются объявленными до тех пор, пока класс, в котором они объявлены, не будет завершен. Таким образом, независимо от того, что foo
является static 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
}
(Обратите внимание, что это на самом деле не является нарушением 3.3.7, поскольку здесь есть только одна правильная программа, которая возможна здесь.)
Такое поведение на самом деле может быть нарушением стандарта; TC указывает (в комментарии ниже), что 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())) { }
Согласно ошибке, noexcept
спецификация для объявления bar2
оценивает noexcept(false)
, который тогда считается несоответствием для noexcept(noexcept(MyClasss::foo2()))
,