[英]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.