我有以下C ++ 17代码:
template <typename Callback = void(*)()>
struct A {
A(Callback c = &noop) {}
private:
static void noop() {}
};
int main() {
A a{};
}
Clang 6编译时没有任何错误,但是GCC 8.2说:
In function ‘A(Callback)-> A<Callback> [with Callback = void (*)()]’:
3:24: error: ‘static void A<Callback>::noop() [with Callback = void (*)()]’ is private within this context
A(Callback c = &noop) {}
^~~~~
5:14: note: declared private here
static void noop() {}
^~~~
有趣的是,如果Callback
不是模板参数,而是简单的类型别名(即,如果我在using Callback = void (*)()
类中编写A
),那么两个编译器都可以。另外,如果我不是显式编写A a{};
而不是A<> a{};
,那么两个编译器都可以。这使我认为这个问题与新的C ++ 17类模板推导机制有关。
我的问题是,哪个编译器符合标准?这是GCC错误还是Clang过于宽容?
答案 0 :(得分:8)
从概念上讲,CTAD是通过合成一组功能和功能模板([over.match.class.deduct]p1)进行的。与此相关,该集合包括 :
如果定义了
C
,则对于C
的每个构造函数,一个函数模板 具有以下属性:
- 模板参数是
C
的模板参数,其后是的模板参数(包括默认模板参数) 构造函数(如果有)。- 函数参数的类型是构造函数的类型。
- 返回类型是由
C
指定的类模板特殊化和与C
的模板参数相对应的模板参数。
值得注意的是,这省略了默认函数参数。这几乎可以肯定是一个缺陷。
不幸的是,此缺陷还意味着没有人从标准中知道访问控制如何作用于综合功能模板的默认函数自变量-甚至不允许它们首先存在。
从设计的角度来看,我希望Clang的行为是正确的。