繁体   English   中英

CRTP继承中的基类的“友谊”是否也会影响子类?

[英]Does “friending” the base class in CRTP inheritance affect the child class as well?

为了尝试回答另一个问题 ,我想出了一个方案来强制CRTP基类的子代在其构造函数中接受特定类型作为参数:将参数类型的构造函数设为private ,将CRTP基类分配为friend ,并将参数类型也声明为基类构造函数的参数。

但是,当我试图证明此方案通过访问冲突提供了所需的保护时,我发现即使参数类型的构造函数是私有的,子类也可以构造它:

template <typename T>
class SingletonBase {
  protected: class P { friend class SingletonBase<T>; P() = default; };
  public:
     SingletonBase(P) {} 
};

class Logger: public SingletonBase<Logger> {
  using BASE = SingletonBase<Logger>;
  public:
    Logger() : BASE{P{}} {} // WHY NO ACCESS VIOLATION?
};

即使我期望访问冲突,该编译也不会出错 为什么?

CRTP继承中的基类的“友谊”是否也会影响子类?

不,当然不。 友谊不是继承的。 为了说明这个问题,

首先, P::P()是默认的默认构造函数,它是一个琐碎的默认构造函数

其次, P{}值初始化 (自C ++ 11起),

(强调我的)

2)如果T是具有默认构造函数的类类型,该构造函数既不是用户提供的也不是用户删除的(也就是说,它可能是带有隐式定义或默认默认构造函数的类),则将该对象初始化为零,然后将其如果具有非平凡的默认构造函数,则默认初始化

请注意,此处仅将其初始化为零 ,而不会默认初始化 P的私有默认构造函数根本不会被调用。

如果T是非联合类类型,则所有基类和非静态数据成员都将初始化为零,并且所有填充都将初始化为零位。 构造函数(如果有)将被忽略。

如果将其明确更改为默认初始化,则会出现访问冲突错误。

Logger() : BASE{P()} {} // error: calling a private constructor of class 'SingletonBase<Logger>::P
//               ~~

简化的示范

class X { X() = default; };

int main()
{
    X x1{}; // fine
    X x2;   // error: calling a private constructor of class 'X'
}

生活

您可以提供用户定义的默认构造函数(这是一个平凡的构造函数)来更改值初始化的行为。

template <typename T>
class SingletonBase {
  protected: 
    class P { 
      friend class SingletonBase<T>; 
      P() {} // user-defined default constructor
    };
  public:
    SingletonBase(P) {} 
};

class Logger: public SingletonBase<Logger> {
  using BASE = SingletonBase<Logger>;
  public:
    Logger() : BASE{P{}} {} // error: calling a private constructor of class 'SingletonBase<Logger>::P'
};

您所做的与friend陈述无关!

如果删除friend ,代码也可以编译!

这是因为用于空类的默认构造函数是public的:

从C ++ 11标准:

如果类X没有用户声明的构造函数,则将不带参数的构造函数隐式声明为默认值。 隐式声明的默认构造函数是其类的内联公共成员。

如果没有这样的默认构造函数:

template <typename T>
class SingletonBase
{
    protected: 
        class P
        { 
            friend class SingletonBase<T>;
            P(int){ }
        };

    public:
        SingletonBase(P) {}
};

class Logger: public SingletonBase<Logger>
{
    using BASE = SingletonBase<Logger>;

    public:
    Logger() : BASE(P{1}) {} // WHY NO ACCESS VIOLATION?
};

您将收到“访问”冲突,并且看到您的friend没有工作!:

main.cpp: In constructor 'Logger::Logger()':
main.cpp:10:17: error: 'SingletonBase<T>::P::P(int) [with T = Logger]' is private
                 P(int){ }
                 ^
main.cpp:22:28: error: within this context
         Logger() : BASE(P{1}) {} // WHY NO ACCESS VIOLATION?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM