什么时候是一个私人的构造不是一个私人的构造函数?

比方说,我有一个types,我想使其默认构造函数私有。 我写了以下内容:

class C { C() = default; }; int main() { C c; // error: C::C() is private within this context (g++) // error: calling a private constructor of class 'C' (clang++) // error C2248: 'C::C' cannot access private member declared in class 'C' (MSVC) auto c2 = C(); // error: as above } 

大。

但是,那么构造函数并不像我想的那样私密:

 class C { C() = default; }; int main() { C c{}; // OK on all compilers auto c2 = C{}; // OK on all compilers } 

这让我感到非常惊讶,意外,明显不受欢迎的行为。 为什么这是好的?

技巧是在C + + 14 8.4.2 / 5 [dcl.fct.def.default]:

…一个函数是用户提供的,如果它是用户声明的,而不是在其第一个声明中显式默认或删除。 …

这意味着C的默认构造函数实际上不是用户提供的,因为它的第一个声明是明确默认的。 因此, C没有用户提供的构造函数,因此是每个8.5.1 / 1 [dcl.init.aggr]的聚合:

聚合是没有用户提供的构造函数(12.1),没有私有或受保护的非静态数据成员(第11章),没有基类(第10章),也没有虚函数(10.3)的数组或类(第9章) )。

你没有调用默认的构造函数,你正在聚合types上使用聚合初始化。 集合types被允许有一个默认的构造函数,只要它在第一次声明的地方是默认的:

从[dcl.init.aggr] / 1 :

聚合是一个数组或类(Clause [class])

  • 没有用户提供的构造函数([class.ctor])(包括从基类inheritance的([namespace.udecl])),
  • 没有私有或受保护的非静态数据成员(Clause [class.access]),
  • 没有虚函数([class.virtual])和
  • 没有虚拟,私有或受保护的基类([class.mi])。

和[dcl.fct.def.default] / 5

显式默认函数和隐式声明函数统称为默认函数,实现应为它们([class.ctor] [class.dtor],[class.copy])提供隐式定义,这可能意味着将它们定义为已删除。 一个函数是用户提供的,如果它是用户声明的,并且没有明确地默认或删除它的第一个声明。 用户提供的显式默认函数(即在第一次声明后显式默认)在其明确默认的地方被定义。 如果这样的function被隐含地定义为删除,则该程序是不合格的。 [注意:在首次声明之后将函数声明为默认函数可以提供高效的执行和简洁的定义,同时为不断发展的代码库提供稳定的二进制接口。 – 结束注意]

因此,我们对于一个总计的要求是:

  • 没有非公众成员
  • 没有虚拟function
  • 没有虚拟或非公共基础课程
  • 没有用户提供的构造函数是inheritance的,否则只允许构造函数是:
    • 隐式声明,或者
    • 明确地声明和定义为默认同时。

C满足所有这些要求。

当然,通过简单地提供一个空的默认构造函数,或者在声明构造函数后将其定义为默认构造函数,就可以摆脱这种错误的默认构造行为:

 class C { C(){} }; // --or-- class C { C(); }; inline C::C() = default;