成员函数的自动推导返回值能否作为友元函数模板的默认实参?给定代码中Clang与GCC的编译分歧谁符合C++标准?
疑问1:成员函数的自动推导返回值能否用作友元函数模板的默认实参?
答案是:要看具体语境——只有当该成员函数的返回类型已经被成功推导,且处于允许使用该类型的语境时,才可以。如果在返回类型还没推导完成的阶段就去用它(比如在类的定义过程中,成员函数刚定义完但类还没闭合时),这种用法不符合标准,会导致程序病态。
疑问2:Clang和GCC的编译差异,哪个符合C++标准?
先看你给出的代码:
struct A{ static auto f() {} template<class T = decltype(A::f())> friend void g(A) {} }; int main(){ g(A{}); }
Clang报错function 'f' with deduced return type cannot be used before it is defined,而GCC能正常编译。这里的核心是对C++标准规则的不同解读:
根据C++标准的[dcl.spec.auto]/10条款:
声明了使用占位符类型作为返回类型的函数,在返回类型被推导出来之前,不能在需要知道该函数类型的语境中使用。
同时,[class.mem]/6补充了类定义过程中的规则:
在类成员声明范围内,除了函数体、默认实参、noexcept说明符、默认成员初始化器(包括嵌套类中的这些内容)之外,类被视为不完整类型。
回到你的代码:当编译器处理友元模板g的默认模板参数decltype(A::f())时,我们还在struct A的内部定义过程中(类还没到闭合的}),此时A属于不完整类型。而f作为带自动推导返回类型的成员函数,它的返回类型推导需要等到类完整定义完成,此时f的返回类型还处于“未推导”状态。
Clang的判断严格遵循标准:此时f的返回类型还没被推导完成,用decltype(A::f())来做默认模板参数属于违规,因此报错。
而GCC则做了一个实用化的扩展:既然f的函数体已经写出来了(空函数体,返回类型必然是void),它就提前推导了f的返回类型,从而允许这个默认参数的使用。
所以结论很明确:Clang的行为完全符合C++标准的要求,GCC的行为属于编译器的非标准扩展。
内容的提问来源于stack exchange,提问作者Fedor

