c++ - 将私有父类类型作为参数传递 (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;
}
解决方案
该站点上反复出现的主题之一是请求一个最小的、完整的和可验证的示例。你在“完整”和“可验证”部分做得很好,但在“最小”部分做得不好。请允许我简化您的代码以删除可能分散注意力的细节。
// 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
肯定是指正在构造的类的(私有)基类。问题:为什么编译器应该假设前一种情况不也引用私有基类?(如果私有基类改变了,参数的类型是否也必然改变?编译器假设“是”,但你可以引入另一个中间类,介于A
and之间B
,它可能会保留参数的类型。)
据我所知(我没有仔细检查语言规范),当您处于 的上下文中时B
,任何提及其私有基类的内容都被视为私有信息。您可以将构造函数的声明视为:B(<private> * a)
. 由于C
不允许知道 的私有信息B
,所以不允许调用这个构造函数。它根本无法匹配参数列表,就像参数的类型是private
在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;
}
在您的情况下,这将具有为该相当丰富的符号引入较短同义词的附带好处std::vector<std::unique_ptr<cComposite>>
。
推荐阅读
- python - 安装 PyGObject 时出错:预处理器命令“mesondefine”无效
- view - SwiftUI + UIView 触摸事件碰撞
- java - 如何使用 super 关键字调用 innerClass 之外的方法
- python-3.x - dash web 应用程序的图像上传内存管理问题
- javascript - 正则表达式 test() 返回 true 但预期为 false
- javascript - 如何通过单击滚动对象数组中的问题?
- playwright - 在 Python 的 Playwright 中,如何使用 QuerySelector 匹配标签内的部分文本?
- javascript - 包含数组的状态未更新
- ios - 崩溃点在哪里?我在火力基地得到以下报告
- python - 循环中的两个函数,但只执行一个(DICOM 文件的匿名化 + 移动它们)