Объявление специалиста constexpr как друга

У меня есть шаблонный шаблон A и шаблонная функция f, которая возвращает объекты A. Я хочу, чтобы f<T> был другом A<T> и все еще был constexpr

template <typename T>
class A;

template <typename T>
constexpr A<T> f();

//f<T> is a friend of A<T>

template <typename T>
class A {
  friend /* constexpr? */ A f<T>();
  constexpr A() {}
};

template <typename T>
constexpr A<T> f() { return {}; }

int main() {
  constexpr auto a  = f<void>();
}

Я не могу получить clang и gcc, чтобы договориться о том, что здесь. Если я не помещаю constexpr в объявление друга, gcc работает нормально, но clang не будет его компилировать, с ошибкой:

main.cpp:18:18: error: constexpr variable 'a' must be initialized by a constant expression
  constexpr auto a  = f<void>();
                 ^    ~~~~~~~~~
main.cpp:18:23: note: non-constexpr function 'f<void>' cannot be used in a constant expression
  constexpr auto a  = f<void>();
                      ^
main.cpp:9:12: note: declared here
  friend A f<T>(); 

Если я помечаю его как constexpr в объявлении друга, clang компилирует отлично, но gcc дает мне ошибку:

main.cpp:9:27: error: 'constexpr' is not allowed in declaration of friend template specialization 'A<T> f<T>()'
   friend constexpr A f<T>();

Как я могу сделать всех счастливыми?

Ответы

Ответ 1

int main() { constexpr auto a  = f<void>(); }

Это специализирует шаблон функции f как функцию f<void>(); во время специализации f компилятор также пытается создать экземпляр A<void>, который, в свою очередь, объявляет специализацию friend f<void>().

Эти два объявления должны соответствовать constexpr:

[dcl.constexpr]/1

[...] Если какое-либо объявление шаблона функции или функции имеет спецификатор constexpr, то все его объявления должны содержать спецификатор constexpr. [Примечание. Явная специализация может отличаться от объявления шаблона в отношении спецификатора constexpr. -end note]

Вероятно, что Clang должен быть ошибочным раньше, когда вы опускаете constexpr в объявлении friend вместо того, чтобы удалять то, что кажется не-t26, но по крайней мере оно принимает правильный синтаксис.

Gcc не должен допускать отсутствие версии constexpr и дает сообщение об ошибке, если вы предоставляете constexpr из-за bug. С тех пор это исправлено в багажнике, и я могу подтвердить, что он работает сейчас, хотя он все еще не дает ошибку, когда constexpr отсутствует.