带有模板化类型的静态私有函数,作为C++中的默认参数

Nik*_*nes 13 c++ gcc language-lawyer c++17

我有以下C++ 17代码:

template <typename Callback = void(*)()>
struct A {
    A(Callback c = &noop) {}
private:
    static void noop() {}
};

int main() {
    A a{};
}
Run Code Online (Sandbox Code Playgroud)

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() {}
              ^~~~
Run Code Online (Sandbox Code Playgroud)

有趣的是,如果Callback不是模板参数而是简单类型别名(即如果我using Callback = void (*)()A类中写入),则两个编译器都可以.另外,如果不是A a{};我明确地写A<> a{};,两个编译器都是好的.这让我认为这个问题以某种方式与新的C++ 17类模板推导机制相关联.

我的问题是,根据标准,哪个编译器是正确的?这是一个GCC错误还是Clang过于宽容?

T.C*_*.C. 8

CTAD通过合成一组函数和函数模板([over.match.class.deduct] p1)来概念性地进行.与此相关,该集包括:

如果C为每个构造C函数定义了具有以下属性的函数模板:

  • 模板参数是模板参数,C后跟构造函数的模板参数(包括默认模板参数),如果有的话.
  • 函数参数的类型是构造函数的类型.
  • 返回类型是由C模板参数指定的类模板特化和与模板参数对应的模板参数C.

值得注意的是,这省略了默认的函数参数.这几乎肯定是一个缺陷.

不幸的是,这个缺陷也意味着没有人从标准中知道访问控制如何对合成函数模板的默认函数参数起作用 - 它甚至不允许它们首先存在.


从设计的角度来看,我希望Clang的行为是正确的.