将constexpr专门化声明为友元

16

我有一个模板类A和一个返回A对象的模板函数f。 我想让f<T>成为A<T>的朋友,并仍然保持constexpr

我有一个模板类A和一个返回A对象的模板函数f。我希望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在这里达成共识。如果在friend声明中不加入constexpr,gcc可以正常工作,但是clang无法编译它,会出现以下错误:

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>();

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


请参考这个问答了解类似错误。 - TemplateRex
1个回答

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

这将模板函数f专门化为函数f<void>(); 在专门化f的过程中,编译器还会尝试实例化A<void>,从而声明专门化的friend f<void>()

这两个声明必须匹配以用于constexpr

[dcl.constexpr] / 1

[...] 如果任何一个函数或函数模板的声明具有constexpr说明符,则其所有的声明都必须包含constexpr说明符。 [注意:显式特化可以与模板声明在constexpr说明符方面存在区别。 — 注解结束]

当您省略friend声明中的constexpr时,Clang可能应该更早地出错,而不是生成看起来是非constexpr函数的代码,但至少它接受了正确的语法。

Gcc不应允许缺少constexpr的版本,并且由于错误,当您提供constexpr时,会产生错误。这个问题已经在trunk中得到了修复,我可以确认现在它已经可行了,尽管当缺少constexpr时它仍然不会提供错误提示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接