Frage

In my code I use a templated multi-dimensional container class array_dyn<T>, which has a property my_data that is a std::vector<T>.

To keep things separable I use my own class bisArray<T> that inherits from 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()) {}

Here, dir_fwd (and dir_rev) represents c (and fortran) storage order. The array_dyn class is here [ https://svn.boost.org/svn/boost/sandbox/variadic_templates/sandbox/array_dyn ].

Happy to provide more code, but I think the problem that I get is caused here: when I use

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

Then there is an error message

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

Previous StackOverflow posts about this error mentioned circular definitions and forward declarations; but that is not what I am doing here. I just inherit from array_dyn, which has a property my_data, but when I create an object with the derived class bisArray, it says that its base class does not have this property.

Am I using the wrong inheritance mechanism? Or the wrong access method?

War es hilfreich?

Lösung

Short answer: You can't initialize base class members in the constructor of a derived class.

You may want to try something like this instead (assuming that my_data has a suitable setter method):

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

Longer version, the best explanation I could find from the C++ standard is in paragraph 12.6.1.10:

In a non-delegating constructor, initialization proceeds in the following order:

— First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

— Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

— Finally, the compound-statement of the constructor body is executed

Now, this may sound a bit round-about, but the constraint that non-static data members are initialized in the order in which they were declared in the class definition implies that you can't initialize base-class members in a derived class.

Andere Tipps

You probably shouldn't be (ab)using inheritance...

Question: why do you use inheritance for bisArray<T>?

Is it to add extra functionality to array_dyn<T> that can entirely be written in the public interface of array_dyn<T>? In that case, add non-member functions to provide that functionality instead of inheriting and adding member functions. So instead of

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

write a non-member function

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

If you need to add functionality that requires extra state to a array_dyn<T>, use composition

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

array_dyn<T> was never designed to be a base class. For one, it doesn't have a virtual destructor. Second, the designers of array_dyn<T> also composed a std::vector<T>, and did not inherit from it, for exactly those reasons.

Of course, when using composition, bisArray<T> will have to redefine the entire interface (members and constructors) that it wants to keep. However, in the words of Alexandrescu and Sutter:

Admittedly, it's tedious to have to write passthrough functions for the member functions you want to keep, but such an implementation is vastly better and safer than using public or nonpublic inheritance.

... but use the base constructors if you do...

OK, you absolutely-positively-definitely want to use inheritance. Well that's easy: just delegate all work to the array_dyn<T> base class constructor:

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

Your two requested constructors can then be gotten from your current version minus the initialization of my_data because the array_dyn<T> constructor already does the exact same amount of work

// 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)
{}     

In case you want to set super_type::my_data to another value than what the super_type constructor does, just put such a statement in the body of the constructor. But from the Boost code and your code alone that doesn't seem to be required here.

... or use factory functions to get the best of both worlds

Still, if all that bisArray<T> does is create a default constructor that takes the C-array style parameter dir_fwd, why not drop the inheritance, and write two non-member factory functions that return a array_dyn<T> with the corresponding constructor arguments

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

which you can call like this:

auto c = make_c_array_dyn<double>(your_sizes);
auto f = make_fortran_array_dyn<double>(your_sizes);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top