C++11派生嵌套类访问基类封闭类受保护成员编译错误问题
你的问题出在C对protected成员访问权限的严格规则上,虽然你觉得DerivedEnclosing继承了m_parameters,它的嵌套类DerivedNested就应该能访问,但Clang的报错其实是完全符合C标准的。让我一步步拆解原因,再给出适配你需求的解决方案:
为什么会报错?
C++中protected成员的访问规则是:
- 派生类(比如
DerivedEnclosing)可以访问基类(BaseEnclosing)的protected成员,但只能是在派生类自身的对象上,或者从派生类继承的子类对象上。 - 你的
DerivedNested是DerivedEnclosing的嵌套类,它试图通过m_baseEncPtr(类型是BaseEnclosing*)访问m_parameters。编译器在编译期只看指针的静态类型——也就是BaseEnclosing*,它无法确定这个指针实际指向的是DerivedEnclosing对象,所以会认为你在直接访问一个BaseEnclosing对象的protected成员。而DerivedNested既不是BaseEnclosing的派生类,也没有通过DerivedEnclosing自身的对象来访问,自然就触发了权限错误。
几种可行的解决方案
方案1:给基类添加访问接口(最优雅,符合封装原则)
在BaseEnclosing中添加一个protected的成员函数,用来暴露你需要的m_parameters内容,这样任何有权限访问基类protected成员的代码(包括你的嵌套类)都可以通过这个接口获取数据:
修改BaseEnclosing的protected部分:
protected : std::vector<double> m_parameters; // 添加这个接口函数 double getLastParameter() const { return m_parameters.back(); } template<class T> class BaseNested { // 原代码不变 };
然后在DerivedNested的operator()里替换成:
double retrieved_bar = m_baseEncPtr->getLastParameter();
这个方案既符合C++的访问规则,又保持了封装性——你不需要暴露整个m_parameters容器,只提供需要的功能即可,也完全适配Boost Odeint对仿函数的要求。
方案2:将指针类型改为DerivedEnclosing*
既然DerivedNested是DerivedEnclosing的嵌套类,你可以把构造函数的参数类型改成DerivedEnclosing*,这样编译器就能确定指针指向的是DerivedEnclosing对象,从而允许访问继承来的protected成员:
修改DerivedNested的定义:
class DerivedNested : public BaseNested<std::vector<double>> { public : // 构造函数参数改为DerivedEnclosing* DerivedNested(DerivedEnclosing* derivedEncPtr) : BaseNested(derivedEncPtr), m_derivedEncPtr(derivedEncPtr) {} void operator()(std::vector<double> myContainer) const override { // 现在可以直接访问了 double retrieved_bar = m_derivedEncPtr->m_parameters.back(); // 你的操作逻辑 } private: DerivedEnclosing* m_derivedEncPtr; // 新增成员保存指针 virtual ~DerivedNested() {} };
然后在DerivedEnclosing::DoSomething()中创建DerivedNested时传入this即可,因为this是DerivedEnclosing*类型。
方案3:使用static_cast转换类型(不推荐,不够安全)
如果你坚持要用BaseEnclosing*,可以在访问时把指针强制转换成DerivedEnclosing*,但这种方式需要你确保指针确实指向DerivedEnclosing对象,否则会有未定义行为:
double retrieved_bar = static_cast<DerivedEnclosing*>(m_baseEncPtr)->m_parameters.back();
这个方案虽然能解决编译问题,但安全性较差,不推荐在生产代码中使用。
适配Boost Odeint的说明
以上三种方案都不会影响你使用Boost Odeint的仿函数方案——DerivedNested依然是一个符合要求的仿函数,只要你在DoSomething()中正确创建它并传递给Odeint的接口即可。
内容的提问来源于stack exchange,提问作者jack

