派生类中的错误&#39; <base class =“”/>&#39;中没有名为my_data的类型

时间:2013-10-14 22:25:00

标签: c++ inheritance boost constructor composition

在我的代码中,我使用模板化的多维容器类array_dyn<T>,其属性my_datastd::vector<T>

为了保持可分离性,我使用自己的bisArray<T>继承的array_dyn课程:

typedef array_dyn<T>                   super_type;
typedef typename super_type::index_t   index_t;

template < typename Sizes >
bisArray( dirs a_dir, Sizes const& a_sizes={} ): 
        super_type ( a_dir, a_sizes ),   super_type::my_data( super_type::size()) {}
template < typename Sizes >
bisArray( Sizes const& a_sizes={} ): 
        super_type ( dir_fwd, a_sizes ), super_type::my_data( super_type::size()) {}

此处,dir_fwd(和dir_rev)表示c(和fortran)存储顺序。 array_dyn类在这里[https://svn.boost.org/svn/boost/sandbox/variadic_templates/sandbox/array_dyn]。

很高兴提供更多代码,但我认为我得到的问题是:当我使用

std::vector<size_t> newsizes({2,3});
bisArray<int>       newarray(newsizes); // using constructor #2

然后出现错误消息

no type named my_data in struct 'array_dyn<int>' 

以前关于此错误的StackOverflow帖子提到了循环定义和前向声明;但这不是我在这里做的。我只是继承自array_dyn,它有一个属性my_data,但是当我用派生类bisArray创建一个对象时,它说它的基类没有这个属性。

我使用错误的继承机制吗?或者错误的访问方法?

2 个答案:

答案 0 :(得分:2)

简短回答: 您无法在派生类的构造函数中初始化基类成员。

你可能想尝试这样的事情(假设my_data有一个合适的setter方法):

template < typename Sizes >
bisArray( dirs a_dir, Sizes const& a_sizes={} ): 
    super_type ( a_dir, a_sizes ) {
    super_type::my_data.set_size( super_type::size());
}

更长版本,我可以从C++ standard找到的最佳解释见第12.6.1.10段:

  

在非委托构造函数中,初始化按以下顺序进行:

     

- 首先,仅对于派生程度最高的类(1.8)的构造函数,虚拟基类按照它们出现在基类的有向非循环图的深度 - 从左到右遍历的顺序进行初始化,其中“从左到右”是派生类base-speci-list中基类出现的顺序。

     

- 然后,直接基类按声明顺序初始化,因为它们出现在基本指定列表中(无论mem-initializer的顺序如何)。

     

- 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不管mem-initializers的顺序如何)。

     

- 最后,执行构造函数体的复合语句

现在,这可能听起来有点四舍五入,但非静态数据成员按照在类定义中声明的顺序进行初始化的约束意味着您无法初始化基类成员衍生类。

答案 1 :(得分:1)

你可能不应该(ab)使用继承...

问题:为什么要为bisArray<T>使用继承?

是否可以向array_dyn<T>添加额外的功能,这些功能可以完全写入array_dyn<T>的公共接口?在这种情况下,添加非成员函数以提供该功能,而不是继承和添加成员函数。而不是

template<class T>
class bisArray: public array_dyn<T>
{
public:
     void doCoolStuffThatBoostDoesNotProvide() const;
};

撰写非会员功能

template<class T>
void doCoolStuffThatBoostDoesNotProvide(array_dyn<T> const& s);

如果您需要向array_dyn<T>添加需要额外状态的功能,请使用合成

template<class T>
class bisArray
{
public: 
     void doCoolStuffThatBoostCannotImplement() const;
private:
     some_fancy_type s_;
     array_dyn<T> a_;
};

array_dyn<T>从未被设计为基类。例如,它没有虚拟析构函数。其次,array_dyn<T>的设计者也组成了一个std::vector<T>,并且没有从中继承,正是出于这些原因。

当然,使用合成时,bisArray<T>必须重新定义它想要保留的整个接口(成员和构造函数)。但是,用Alexandrescu and Sutter

的话来说
  

不可否认,必须编写passthrough函数是很乏味的   你想要保留的成员函数,但是这样的实现是   比使用公共或非公共继承更好更安全。

...但如果你这样做,那就使用基础构造函数......

好的,你绝对肯定想要使用继承。嗯,这很简单:只需将所有工作委托给array_dyn<T>基类构造函数:

template
  < typename Sizes
  >
array_dyn( dirs a_dir, Sizes const& a_sizes={})
: super_t( a_dir, a_sizes)
, my_data( super_t::size())
{
}

然后可以从当前版本中减去my_data的初始化,因为array_dyn<T>构造函数已完成相同的工作量

// note use explicit keyword for single-argument constructors
template<typename Sizes>
explicit bisArray( dirs a_dir, Sizes const& a_sizes={} )
: super_type(a_dir, a_sizes) 
{}

template < typename Sizes >
bisArray( Sizes const& a_sizes={} )
: super_type(dir_fwd, a_sizes)
{}     

如果你想将super_type::my_data设置为另一个值而不是super_type构造函数的值,只需将这样的语句放在构造函数的主体中即可。但是从Boost代码和你自己的代码来看,这里似乎并不需要。

...或使用工厂功能来充分利用这两个世界

但是,如果所有bisArray<T>做的是创建一个采用C数组样式参数dir_fwd的默认构造函数,为什么不删除继承,并编写两个返回a的非成员工厂函数array_dyn<T>带有相应的构造函数参数

template<typename T, typename Sizes>
make_fortran_array_dyn(Sizes const& a_sizes={} )
{
    return array_dyn<T>(dir_rev, a_sizes);
}

template <typename T, typename Sizes >
make_c_array_dyn( Sizes const& a_sizes={} )
{
     return array_dyn<T>(dir_fwd, a_sizes);
}     

你可以像这样打电话:

auto c = make_c_array_dyn<double>(your_sizes);
auto f = make_fortran_array_dyn<double>(your_sizes);