簡體   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