Функция `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())),

Другие вопросы по тегам