"friending" CRTP 继承中的基 class 是否也会影响子 class?
Does "friending" the base class in CRTP inheritance affect the child class as well?
在 attempt to answer another question 中,我想出了一个方案来强制 CRTP 基 class 的子级接受特定类型作为其构造函数中的参数:使参数类型的构造函数 private
,将 CRTP 基 class 分配为 friend
,并将参数类型声明为基 class 构造函数的参数。
然而,当我试图证明该方案通过访问冲突提供了所需的保护时,我发现即使参数类型的构造函数是私有的,子 class 也能够构造它:
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?
};
这个 compiles without error,尽管我预计会发生访问冲突。为什么?
你所做的与你的friend
声明无关!
如果删除 friend
代码编译也很好!
这是因为空 class 的默认构造函数是 public:
来自 C++11 标准:
If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted. An implicitly-declared default constructor is an inline public member of its class.
如果你没有像这样的默认构造函数:
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?
};
您将遇到 "access" 违规,您会发现您的 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?
Does “friending” the base class in CRTP inheritance affect the child class as well?
不,当然不是。友情不是世代相传的。为了说明这个问题,
首先,P::P()
是一个默认的默认构造函数,它是一个trivial default constructor。
其次,P{}
是 value initialization (C++11 起),
(强调我的)
2) if T
is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;
请注意,只有 zero initialized here, not default initializated。 P
的私有默认构造函数根本不会被调用。
If T
is an non-union class type, all base classes and non-static data members are zero-initialized, and all padding is initialized to zero bits. The constructors, if any, are ignored.
如果您将其显式更改为默认初始化,则会出现访问冲突错误。
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'
};
在 attempt to answer another question 中,我想出了一个方案来强制 CRTP 基 class 的子级接受特定类型作为其构造函数中的参数:使参数类型的构造函数 private
,将 CRTP 基 class 分配为 friend
,并将参数类型声明为基 class 构造函数的参数。
然而,当我试图证明该方案通过访问冲突提供了所需的保护时,我发现即使参数类型的构造函数是私有的,子 class 也能够构造它:
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?
};
这个 compiles without error,尽管我预计会发生访问冲突。为什么?
你所做的与你的friend
声明无关!
如果删除 friend
代码编译也很好!
这是因为空 class 的默认构造函数是 public:
来自 C++11 标准:
If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted. An implicitly-declared default constructor is an inline public member of its class.
如果你没有像这样的默认构造函数:
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?
};
您将遇到 "access" 违规,您会发现您的 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?
Does “friending” the base class in CRTP inheritance affect the child class as well?
不,当然不是。友情不是世代相传的。为了说明这个问题,
首先,P::P()
是一个默认的默认构造函数,它是一个trivial default constructor。
其次,P{}
是 value initialization (C++11 起),
(强调我的)
2) if
T
is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;
请注意,只有 zero initialized here, not default initializated。 P
的私有默认构造函数根本不会被调用。
If
T
is an non-union class type, all base classes and non-static data members are zero-initialized, and all padding is initialized to zero bits. The constructors, if any, are ignored.
如果您将其显式更改为默认初始化,则会出现访问冲突错误。
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'
};