将 constexpr 特化声明为友元

Declaring a constexpr specialization as friend

我有一个模板化的 class A 和一个模板化的函数 f returns 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>();

我怎样才能让每个人都开心?

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

这将函数模板 f 特化为函数 f<void>();在 f 的特化期间,编译器还尝试实例化 A<void>,后者又声明特化 friend f<void>().

这两个声明必须匹配constexpr:

[dcl.constexpr] / 1

[...] If any declaration of a function or function template has a constexpr specifier, then all its declarations shall contain the constexpr specifier. [Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. —end note]

当您在 friend 声明中省略 constexpr 时,Clang 可能会更早地出错,而不是消除看似非 constexpr 函数的内容,但至少它接受正确的语法。

Gcc 不应允许缺少 constexpr 的版本,并且当您提供 constexpr 时由于 bug 会给出错误。这已经在主干中得到修复,我可以确认它现在可以工作,尽管当缺少 constexpr 时它仍然没有提供错误。