1

在我的代码中,我使用了一个模板化的多维容器类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(and dir_rev) 表示 c (and 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 创建一个对象时,它说它的基类没有这个属性。

我是否使用了错误的继承机制?还是访问方式不对?

4

2 回答 2

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++ 标准中找到的最佳解释在第 12.6.1.10 段中:

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

— 首先,并且仅对于最派生类(1.8)的构造函数,虚拟基类按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“左-to-right”是派生类基类说明符列表中基类的出现顺序。

— 然后,直接基类按照它们出现在基说明符列表中的声明顺序进行初始化(不管 mem-initializers 的顺序如何)。

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

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

现在,这听起来可能有点迂回,但非静态数据成员按照它们在类定义中声明的顺序进行初始化的约束意味着您不能在派生类中初始化基类成员。

于 2013-10-15T03:29:54.943 回答
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);

如果您需要向 a 添加需要额外状态的功能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 和 Sutter的话来说:

诚然,必须为要保留的成员函数编写传递函数很乏味,但这样的实现比使用公共或非公共继承要好得多,也更安全。

...但如果您这样做,请使用基本构造函数...

好的,您绝对肯定要使用继承。这很简单:只需将所有工作委托给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,为什么不放弃继承,并编写两个非成员工厂函数返回 aarray_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);
于 2013-10-18T20:58:38.110 回答