将私有父类类型作为参数传递(C2247)

时间:2019-02-26 13:38:08

标签: c++ inheritance

关于私有继承,我有一个很特殊的问题。我也有解决此问题的方法,但是我不明白为什么会起作用。

TL; DR

为什么在某个中间级别上的私有继承会阻止我将基类型作为参数传递给(私有)派生的类?

考虑以下代码(也可以在http://cpp.sh/3p5zv5处获得):我有一个复合类型的类,该类可以包含指向其自身类型的子元素的指针。同样,该类包含模板方法MyMethodTemplate(T value),允许使用任何类型的Parameter。我需要多次从此类继承,这样MyMethodTemplate(T)不可用,而只能用例如调用类型化版本MyMethod()。整数,字符串等。

由于派生类将包含很多样板代码(此处未显示),因此我编写了从cComposite私有继承的类模板cSpecializedComposite(成功隐藏了MyMethodTemplate())。其方法MyMethod()从其父类内部调用MyMethodTemplate()。到目前为止,一切都很好。

现在,要摆脱最终用户代码中的template参数,我想编写从该模板公开继承的普通类(cSpecializedCompositeInt,cSpecializedCompositeString等)。我的假设是cSpecializedCompositeInt会知道cSpecializedComposite的接口,但不知道其内部。在cSpecializedCompositeInt的构造函数中,我可以有选择地传递一个unique_ptr向量,该向量传递给它的父构造函数(谁知道上帝对此有什么了解,在这里看不到,继续前进)。请注意,就cSpecializedCompositeInt而言,即使它不继承cSpecializedCompositeInt,cComposite的类定义也是可见的。

但是,我在cSpecializedCompositeInt的构造函数中收到一个编译器错误C2247,告诉我我不能使用cComposite,因为cSpecializedComposite私自继承自它。这在msvc10和GCC 4.9.2(http://cpp.sh之后的编译器)上均发生。

将私有继承更改为保护后,cSpecializedCompositeInt可以知道它是间接从cComposite继承的,并且编译器错误也消失了。

这与Private Inheritance and Derived Object to Base reference有多大关系?

#include <vector>
#include <memory>

class cComposite
{
public:
  cComposite(std::vector<std::unique_ptr<cComposite>>&& vecC)
    : m_vecC(std::move(vecC))
  {
    //empty
  }

  template <typename T>
  void MyTemplateMethod(T value)
  {
    //do something with any type of value
  }

private:
  std::vector<std::unique_ptr<cComposite>> m_vecC;
};

template <typename MySpecialType>
class cSpecializedComposite : private cComposite
{
public:
  cSpecializedComposite(std::vector<std::unique_ptr<cComposite>>&& vecC)
    : cComposite(std::move(vecC))
  {
    //empty
  }

  void MyMethod(MySpecialType value)
  {
    //allow only MySpecialType as Input, call base class template method to do something
    cComposite::MyTemplateMethod(value);
  }
};

class cSpecializedCompositeInt : public cSpecializedComposite<int>
{
public:
  cSpecializedCompositeInt(std::vector<std::unique_ptr<cComposite>>&& vecC)
    : cSpecializedComposite(std::move(vecC))
  {
    //empty
  }
};

int main(int argc, char* argv[])
{
  std::vector<std::unique_ptr<cComposite>> vecC;
  cSpecializedCompositeInt spec(std::move(vecC));

  spec.MyMethod(5);

  return 0;
}

1 个答案:

答案 0 :(得分:1)

此网站上重复出现的主题之一是请求Minimal, Complete, and Verifiable example。您的“完整”和“可验证”部分确实做得很好,但“最小”部分却做得不多。请允许我简化您的代码,以删除可能分散注意力的细节。

// Base class, constructed from a type that involves itself.
class A {
public:
    A(A *) {}
};

// Intermediate class, derives privately from the base class.
class B : private A {
public:
    B(A * a) : A(a) {}
};

// Most derived class, same constructor parameter as the base class.
class C : public B {
public:
    C(A * a) : B(a) {}  /// error: 'class A A::A' is inaccessible
};

int main(int argc, char* argv[])
{
  return 0;
}

请注意缺少模板和向量;那些只是红鲱鱼。另外,对使用原始指针的道歉;它们只是以最小的开销/行李介绍问题的便捷方式。 (我会使用引用,但这会使构造函数之一变成复制构造函数,这是不明智的。)

查看B的构造函数的定义。在该定义中,“ A”被使用两次:一次作为参数类型的一部分,一次在初始化列表中。对于后一种情况,A的使用肯定是指所构造类的(私有)基类。问题:为什么编译器应该假定前一种情况也不引用私有基类? (如果更改了私有基类,那么参数的类型是否也会随之改变?编译器假定为“是”,但是您可以在AB之间引入另一个中间类,该中间类大概会保留参数的类型。)

据我所知(我尚未仔细检查语言规范),当您在B的上下文中时,凡提及其私有基类的内容均视为私有信息。您可以将构造函数的声明视为:B(<private> * a)。由于不允许C知道B的私有信息,因此不允许调用此构造函数。它根本无法匹配参数列表,这与在private的{​​{1}}部分中定义参数的类型非常相似。幸运的是,可以通过从参数列表中删除对“ B”的提及来避免这种情况。

以下,唯一的变化是A的引入和使用。

typedef

在您的情况下,这会带来一些副作用,即为符号丰富的class A; typedef A * special_type; // Base class, constructed from a type that *indirectly* involves itself. class A { public: A(special_type) {} }; // Intermediate class, derives privately from the base class. class B : private A { public: B(special_type a) : A(a) {} }; // Most derived class, same constructor parameter as the base class. class C : public B { public: C(special_type a) : B(a) {} /// no error! }; int main(int argc, char* argv[]) { return 0; } 引入较短的同义词。