首页 > 解决方案 > 将私有父类类型作为参数传递 (C2247)

问题描述

关于私有继承,我有一个非常小众的问题。我也有这个问题的解决方案,但我不明白它为什么会起作用。

TL;博士

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

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

由于派生类将包含大量样板代码(此处未显示),我编写了从 cComposite 私有继承的类模板 cSpecializedComposite(成功隐藏了 MyMethodTemplate())。它的方法 MyMethod() 在内部从其父类调用 MyMethodTemplate()。到现在为止还挺好。

现在,为了摆脱最终用户代码中的模板参数,我想编写从模板公开继承的普通类(cSpecializedCompositeInt,cSpecializedCompositeString,...)。我的假设是 cSpecializedCompositeInt 会知道 cSpecializedComposite 的接口,但不知道它的内部。在 cSpecializedCompositeInt 的构造函数中,我可以选择传递一个 unique_ptr 的向量,该向量将传递给它的父构造函数(天知道用它做什么,这里没什么可看的,继续前进)。请注意,cComposite 的类定义对 cSpecializedCompositeInt 是可见的,即使 cSpecializedCompositeInt 没有从它继承,据它所知。

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

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

这与Private Inheritance 和 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;
}

标签: c++inheritance

解决方案


该站点上反复出现的主题之一是请求一个最小的、完整的和可验证的示例。你在“完整”和“可验证”部分做得很好,但在“最小”部分做得不好。请允许我简化您的代码以删除可能分散注意力的细节。

// 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's 构造函数的定义。在该定义中,“ A”被使用了两次:一次作为参数类型的一部分,一次在初始化列表中。对于后一种情况,使用A肯定是指正在构造的类的(私有)基类。问题:为什么编译器应该假设前一种情况不也引用私有基类?(如果私有基类改变了,参数的类型是否也必然改变?编译器假设“是”,但你可以引入另一个中间类,介于Aand之间B,它可能会保留参数的类型。)

据我所知(我没有仔细检查语言规范),当您处于 的上下文中时B,任何提及其私有基类的内容都被视为私有信息。您可以将构造函数的声明视为:B(<private> * a). 由于C不允许知道 的私有信息B,所以不允许调用这个构造函数。它根本无法匹配参数列表,就像参数的类型是privateB. 幸运的是,这可以通过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;
}

在您的情况下,这将具有为该相当丰富的符号引入较短同义词的附带好处std::vector<std::unique_ptr<cComposite>>


推荐阅读