CRTP 继承中的基类"friending"是否也会影响子类?
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
是一个类类型,它的默认构造函数既不是用户提供的,也不是被删除的(也就是说,它可能是一个具有隐式定义的或默认的默认构造函数的类),对象被零初始化,然后如果它有一个非平凡的默认构造函数,它被默认初始化;
注意这里只初始化为0,而不是默认初始化。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'
}
生活<<p> 解决方案/strong> 您可以提供一个用户定义的默认构造函数,这是一个重要的构造函数,以改变值初始化的行为。
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?
- 继承期间显示未知行为的子类
- 通过指向指针数组的指针访问子类的属性
- 从父类方法返回子类对象
- c++, 在子类中,如何在没有对象的情况下访问父类的方法?
- 将父类对象强制转换为子类的问题
- 避免在C++中重复子类定义
- 将QOpenGLWidget子类转换为使用Metal而不是OpenGL的子类是否可行?
- 如何初始化矢量的模板化子类
- C++ 继承:将子类传递给需要基类的函数并获取子类行为
- 有没有办法按值将纯抽象类的所有子类传递给 C++ 中的函数?
- 使用子类覆盖基类中定义的函数
- 子类地址等于虚拟基类地址?
- 重载运算符的范围是什么?它是否会影响作为类成员的集合的插入函数?
- 将子类方法声明为基类的友元
- C++子类共享变量?
- 如何检查模板专用化是否是基本模板的子类?
- 仅让特定类'Fabric'构造类'Foo'及其所有子类的实例
- 使用模板参数重载C++方法:如何使其适用于模板的子类?
- 私有基类通过子类中的模板友元函数和模板方法影响成员访问
- CRTP 继承中的基类"friending"是否也会影响子类?