Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是不是;“友谊”;CRTP继承中的基类也会影响子类吗?_C++_Inheritance_Private_Friend_Crtp - Fatal编程技术网

C++ 是不是;“友谊”;CRTP继承中的基类也会影响子类吗?

C++ 是不是;“友谊”;CRTP继承中的基类也会影响子类吗?,c++,inheritance,private,friend,crtp,C++,Inheritance,Private,Friend,Crtp,在一个示例中,我提出了一个方案,强制CRTP基类的子类在其构造函数中接受一个特定类型作为参数:使参数类型的构造函数私有,将CRTP基类指定为友元,并将参数类型声明为基类构造函数的参数 然而,当我试图证明此方案通过访问冲突提供了所需的保护时,我发现即使参数类型的构造函数是私有的,子类也能够构造它: template <typename T> class SingletonBase { protected: class P { friend class SingletonBase&l

在一个示例中,我提出了一个方案,强制CRTP基类的子类在其构造函数中接受一个特定类型作为参数:使参数类型的构造函数
私有
,将CRTP基类指定为
友元
,并将参数类型声明为基类构造函数的参数

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

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?
};
模板
类单音基音{
受保护:类P{friend类SingletonBase;P()=default;};
公众:
单碱基(P){}
};
类记录器:公共单音基{
使用BASE=SingletonBase;
公众:
Logger():BASE{P{}}{}//为什么没有访问冲突?
};

这,即使我预计访问会被违反。为什么?

你所做的与你的
朋友的声明无关

如果删除您的
朋友
,代码也可以编译

这是因为空类的默认构造函数是公共的:

从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?
};
CRTP继承中基类的“交友”是否也会影响子类

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

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

其次,
p{}
is(自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'
};
模板
类单音基音{
受保护的:
类P{
朋友级单音基音;
P(){}//用户定义的默认构造函数
};
公众:
单碱基(P){}
};
类记录器:公共单音基{
使用BASE=SingletonBase;
公众:
Logger():BASE{P{}}{}//错误:调用类'SingletonBase::P'的私有构造函数
};

…但是显式声明构造函数
default
就是声明构造函数。我认为这不算“没有用户声明的构造函数”;事实上,我认为这就是为什么他们用这样的措辞而不是说“没有用户定义的构造函数”。因此,简而言之,使用
=default
似乎为“私有构造函数”把戏提供了一个访问说明符漏洞。从
=default
切换到
{}
是否可以确保非好友不能从头构造类?@KyleStrand-Yes。关键是,用户定义的构造函数是非平凡的,这将改变值初始化的行为。如果您将其作为解决方案添加(因为目标是防止非
friend
类创建对象的实例),我将接受答案。@KyleStrand当然,已添加。
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'
};