派生类中的错误“<base class> 中没有名为 my_data 的类型”

Posted

技术标签:

【中文标题】派生类中的错误“<base class> 中没有名为 my_data 的类型”【英文标题】:error in derived class 'no type named my_data in <base class>' 【发布时间】:2013-10-14 22:25:00 【问题描述】:

在我的代码中,我使用了一个模板化的多维容器类array_dyn&lt;T&gt;,它有一个属性my_data,即std::vector&lt;T&gt;

为了保持分离,我使用了我自己的类 bisArray&lt;T&gt;,它继承自 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>' 

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

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

【问题讨论】:

我认为问题在于你只能在 mem-initializer-list 中初始化你自己类的成员。 【参考方案1】:

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

您可能想尝试这样的事情(假设 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-specifier-list 中的声明顺序进行初始化(无论 mem-initializers 的顺序如何)。

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

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

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

【讨论】:

啊,感谢您的简短回答!好吧,既然 super_type::my_data 是一个 std::vector,我猜那只是 my_data.resize()? 我可以让它工作的唯一方法是使用super_type::my_data.resize( super_type::size()) ;——知道为什么需要super_type::吗?新类继承了my_data,所以这不是必需的吗? @alle_meije 一个依赖基类的成员(my_data 的基类array_dyn&lt;T&gt; 在这种情况下依赖于模板T)必须通过键入this-&gt; 来消除歧义,@ 987654330@ 或其别名super_type::。请注意,Visual C++ 会让你侥幸逃脱,但在 g++/clang 上,标准在这一点上被强制执行。【参考方案2】:

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

问题:为什么bisArray&lt;T&gt; 使用继承?

是不是给array_dyn&lt;T&gt;增加额外的功能,完全可以写在array_dyn&lt;T&gt;的公共接口里?在这种情况下,添加非成员函数来提供该功能,而不是继承和添加成员函数。所以不是

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&lt;T&gt; 添加需要额外状态的功能,请使用 composition

template<class T>
class bisArray

public: 
     void doCoolStuffThatBoostCannotImplement() const;
private:
     some_fancy_type s_;
     array_dyn<T> a_;
;

array_dyn&lt;T&gt; 从未设计为基类。一方面,它没有虚拟析构函数。其次,array_dyn&lt;T&gt; 的设计者也编写了一个std::vector&lt;T&gt;,并没有继承它,正是出于这些原因。

当然,当使用组合时,bisArray&lt;T&gt; 将不得不重新定义它想要保留的整个接口(成员和构造函数)。不过,用Alexandrescu and Sutter的话来说:

诚然,为 您要保留的成员函数,但这样的实现是 比使用公共或非公共继承要好得多,也更安全。

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

好的,您绝对-肯定-肯定要使用继承。这很简单:只需将所有工作委托给array_dyn&lt;T&gt; 基类构造函数即可:

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&lt;T&gt; 构造函数已经完成了完全相同的工作量

// 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&lt;T&gt; 所做的只是创建一个采用 C 数组样式参数 dir_fwd 的默认构造函数,为什么不放弃继承,并编写两个返回 array_dyn&lt;T&gt; 的非成员工厂函数对应的构造函数参数

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);

【讨论】:

感谢@TemplateRex 提供如此广泛的概述!访问您的观点:[1] 这个想法是向 bisArray 添加更多功能(如所说的部分实现),这可能需要一个新类(因此在构造函数中不初始化基类成员的继承是要走的路)[2 ] array_dyn 不继承自vector,但它继承自box_domain(它提供了没有存储的索引)。 [3] 为什么组合比继承更好/安全?跟性能有关系吗? [4] 工厂示例看起来和组合一样吗? @alle_meije [1] 初始化基成员最好通过基构造函数完成,如上所示。如果新功能可以用array_dyn&lt;T&gt;的公共接口实现,最好写非成员函数。 This increases encapsulation。 [4] 工厂方式不需要新的类模板bisArray&lt;T&gt;,只是简单地根据其构造函数定义一个非成员函数模板make_C_array_dyn&lt;T&gt; @alle_meije [2]/[3]:overuse of inheritance 是一个严重的设计错误,但在这个范围内很难解释。 Alexandrescu & Sutter 有几个项目专门用于它。 @alle_meije 来自 A&S 的两件事:a)“从一个类型继承可能会导致名称查找拉入与该类型在同一命名空间中定义的函数和函数模板。这是非常微妙且难以调试的。” b)“从没有虚拟析构函数的类继承可能会通过删除指向实际指向派生对象的基类的指针而使代码充满未定义的行为。编译器和内存分配器似乎可以容忍这种未定义的行为,但它让您陷入无声错误、内存泄漏、堆损坏和移植噩梦的黑暗沼泽中。”

以上是关于派生类中的错误“<base class> 中没有名为 my_data 的类型”的主要内容,如果未能解决你的问题,请参考以下文章

获取派生类以显示与派生类的父类具有聚合关系的抽象类中的变量的 C++ 错误

派生类中的重载构造函数

无法访问派生类中的受保护方法

访问派生类中的受保护成员

如何在 C++ 中访问派生类中的变量?

派生类中的静态方法可以在 C++ 中调用受保护的构造函数吗?