partial specialization with inheritance. Can I avoid inheritance? - c++

I am writing a vector class and I would like it to have the following characteristics:
Use static allocation on the stack whenever possible (to avoid calling new for efficiency).
Be able to be instantiated from a pointer if the user prefers to provide a previously allocated array.
The class needs to be easily converted to a simple pointer. This allows to use previously written routines in C.
Find below this simple test problem with the solution I came up with. I use inheritance so Vector inherits from Vector_base which provides a common interface (pure virtual) for all vectors.
Then I define an empty class Vector that allows me then using partial specialization to have different storage schemes; static or dynamic.
The idea behind this is that I just want vector to be a C++ wrapper to the old-fashioned static array.
I like the implementation below. I'd like to keep the interface I came up with in main.
What I don't like is that sizeof(Vector3) = 32 when in C a vector of three doubles is 24 bytes. The reason for this is the extra 8 bytes of the virtual table.
My question: can I somehow come up with another design that would provide me with the same interface but the vector only has 24 bytes?
Summarizing:
I'd like have a Vector3 of 24 bytes, as in C.
I still want to have arbitrarily large vectors though (with <double,n>)
I'd like to keep the interface used in main().
Could I use a programming idiom like traits or polices for this? I am very new to those and I don't know if they could provide a solution.
Find my little test code below:
#include <iostream>
using namespace std;
#define TRACE0(a) cout << #a << endl; a;
#define TRACE1(a) cout << #a "=[" << a << "]" << endl;
enum alloc_type {Static,Dynamic};
template <class T>
class Vector_base{
public:
Vector_base(){}
virtual operator T*() = 0;
virtual T operator[](int i)const = 0;
virtual T& operator[](int i) = 0;
virtual int size() const = 0;
friend ostream& operator<<(ostream &os,const Vector_base& v){
for (int i=0; i<v.size(); i++)
cout << v[i] << endl;
return os;
}
};
// base template must be defined first
template <class T, int n,alloc_type flg=Static>
class Vector{};
//Specialization for static memory allocation.
template <class T, int n>
class Vector<T,n,Static>: public Vector_base<T>{
public:
T a[n];
public:
Vector() {
for (int i=0; i<n; i++) a[i] = 0;
}
int size()const{return n;}
operator T*(){return a;}
T operator[](int i)const {return a[i];}
T& operator[](int i){return a[i];}
};
//Specialization for dynamic memory allocation
template <class T,int n>
class Vector<T,n,Dynamic>: public Vector_base<T>{ //change for enum. flg=0 for static. flg=1 for dynamic. Static by default
public:
T* a;
public:
Vector():a(NULL){
}
Vector(T* data){ //uses data as its storage
a = data;
}
int size()const{return n;}
operator T*(){return a;}
T operator[](int i)const {return a[i];}
T& operator[](int i){return a[i];}
};
//C++11 typedefs to create more specialized three-dimensional vectors.
#if (__cplusplus>=201103L)
template <typename Scalar,alloc_type flg=Static>
using Vector3 = Vector<Scalar,3,flg>;
#else
#error A compiler with the C++2011 standard is required!
#endif
int main(){
cout << "Testing Vector3: " << endl;
//Vector<double,3> v3;
Vector3<double> v3;
TRACE0(cout << v3 << endl;);
TRACE1(sizeof(v3));
//Vector<double,3,Dynamic> v0(v3);
Vector3<double,Dynamic> v0(v3); //calls Vector<double,3,Dynamic>::Vector(double*) and uses the conversion operator on v3.
TRACE1(sizeof(v0));
TRACE1(sizeof(double*));
TRACE0(v3[1] = 2.1;);
TRACE0(cout << v0 << endl;);
return 0;
}

All the features you want are offered as Standard or can be plugged in to existing Standard extension points.
Use static allocation on the stack whenever possible (to avoid calling new for efficiency).
Meet std::array<T, N>. It's a C++ wrapper on a C array and presents all the same characteristics.
Be able to be instantiated from a pointer if the user prefers to provide a previously allocated array.
Meet Allocators. You can code an allocator that meets the requirement that gives back already allocated memory, then simply use std::vector. Such an allocator is under consideration for future Standards along with other allocator enhancements like polymorphic allocators.
The class needs to be easily converted to a simple pointer. This allows to use previously written routines in C.
Both std::vector and std::array offer this as a triviality.
If you want to offer this choice at runtime, consider boost::variant. Rolling your own discriminated union- not advised.

If I understand you correctly, something like LLVM's SmallVector seems to fit the bill. It has a template parameter declaring the maximum size you want allocated on the stack, and switches to heap memory only when it grows outside that range.
If it doesn't fit your interface directly, I'm sure looking at the implementation will be very useful towards writing something similar yourself.

You are talking about two policies for locating the data: either inline as a small-array optimization, or via indirection with a pointer to a dynamically allocated buffer.
There are two ways to make that policy choice: With static type information, or dynamically. The dynamic choice requires storage to indicate whether any particular vector uses the static or dynamic policy.
For a vector of doubles, you can perhaps use an illegal value in the first element (a NaN encoding) to indicate that the dynamic policy is in effect (the pointer needs to be stored overlapping the remaining elements, use a union for this).
But in other data types, all possible bit patterns are valid. For those you will require additional storage to select the policy. You might know for a particular problem that a particular bit isn't needed for the range of values, and can be used as a flag. But there's no general solution applicable to all data types.
You probably want to look at implementations of the "small string optimization". They are making the same tradeoff for improved locality of reference when the data is small enough to store directly inside the object, and also generally trying to avoid using mode space than necessary.
One thing is for sure. In order to avoid significantly increase in space requirements, you're going to need close coupling. No specialization, no inheritance, just one monolithic class that implements both policies.

Ok guys. It took me all day but this is the solution I came up with and it does exactly what I want. Please share your comments and suggestions to this solution.
Of course I didn't implement all methods I want. I only implemented two fake dot products to show how specific C implementations are chosen at compile time by use of templates.
The scheme is quite more complex than what I thought it would be. The basic concepts I'm using to accomplish my design requirements are:
the curiously recurring template pattern.
Partial specialization
Traits
Compile-time selection with templates (see how I decide what dot product implementation to use).
Again, thanks and please comment!! See code below
#include <iostream>
using namespace std;
#include <type_traits>
//C++11 typedefs to create more specialized three-dimensional vectors.
#if (__cplusplus<201103L)
#error A compiler with the C++2011 standard is required!
#endif
template<class T>
struct traits{};
#define TRACE0(a) cout << #a << endl; a;
#define TRACE1(a) cout << #a "=[" << a << "]" << endl;
enum {Dynamic = -1};
template<typename T,int n>
struct mem_model{
typedef T array_model[n];
};
//Specialization to Dynamic
template<typename T>
struct mem_model<T,Dynamic>{
typedef T* array_model;
};
template<class derived_vector>
struct Vector_base: public traits<derived_vector>{ //With traits<derived_vector> you can derive the compile time specifications for 'derived_vector'
typedef traits<derived_vector> derived;
typedef typename traits<derived_vector>::Scalar Scalar;
public:
inline int size()const{ //Calling derived class size in case a resize is done over a dynamic vector
return static_cast<const derived_vector*>(this)->size(); //derived_vector MUST have a member method size().
}
inline operator Scalar*(){return a;} //All vectors reduce to a Scalar*
inline bool IsStatic()const{return (n==Dynamic)? false: true;}
inline int SizeAtCompileTime()const{return n;} //-1 for dynamic vectors
protected:
using derived::n; //compile time size. n = Dynamic if vector is requested to be so by the user.
typename mem_model<Scalar,n>::array_model a; //All vectors have a Scalar* a. Either static or dynamic.
};
//Default static
template<typename Scalar,int n>
class Vector:public Vector_base<Vector<Scalar,n> >{ //Vector inherits main interface from Vector_base
public:
//Constructors
Vector(){
//do nothing for fast instantiation
}
Vector(const Scalar& x,const Scalar& y,const Scalar& z){
a[0] = x; a[1] = y; a[2] = z;
}
//
inline int size()const{return n;}
private:
using Vector_base<Vector<Scalar,n> >::a;
};
//Traits specialization for Vector. Put in an inner_implementation namespace
template<typename _Scalar,int _n>
struct traits<Vector<_Scalar,_n> >{
typedef _Scalar Scalar;
enum{
n = _n
};
};
double clib_dot_product_d(const int n,double* a,double* b){
double dot = 0.0;
for(int i=0;i<n;i++)
dot += a[i]*b[i];
return dot;
}
float clib_dot_product_f(const int n,float* a,float* b){
cout << "clib_dot_product_f" << endl;
return 1.0;
}
template<typename Scalar>
struct dot_product_selector{};
template<>
struct dot_product_selector<double>{
template<class derived1,class derived2>
static double dot_product(Vector_base<derived1> &a,Vector_base<derived2> &b){
return clib_dot_product_d(a.size(),a,b);
}
};
template<>
struct dot_product_selector<float>{
template<class derived1,class derived2>
static float dot_product(Vector_base<derived1> &a,Vector_base<derived2> &b){
return clib_dot_product_f(a.size(),a,b);
}
};
template<class derived1,class derived2>
typename Vector_base<derived1>::Scalar dot_product(Vector_base<derived1> &a,Vector_base<derived2> &b){
//run time assert checking the two sizes are the same!!
//Compile time (templates) check for the same Scalar type
static_assert( std::is_same<typename Vector_base<derived1>::Scalar,typename Vector_base<derived2>::Scalar>::value,"dot product requires both vectors to have the same Scalar type");
return dot_product_selector<typename Vector_base<derived1>::Scalar>::dot_product(a,b);
}
#if 0
template <typename Scalar,alloc_type flg=Static>
using Vector3 = Vector<Scalar,3,flg>;
#endif
int main(){
cout << "Testing Vector3: " << endl;
Vector<double,3> as;
Vector<double,Dynamic> ad;
TRACE1(sizeof(as));
TRACE1(sizeof(ad));
TRACE1(as.SizeAtCompileTime());
TRACE1(ad.SizeAtCompileTime());
Vector<double,3> u(1,2,3),v(-1,1,5);
Vector<float,3> uf,vf;
TRACE1(dot_product(u,v));
dot_product(uf,vf);
//dot_product(u,vf); //this triggers a compile time assertion using static_assert
return 0;
}

You could simplify the Vector template specialization to...
template <class T, std::size_t Size = -1>
class Vector {
// The statically allocated implementation
};
template <class T>
class Vector<T, -1> {
// The dynamically allocated implementation
};
The implementations could possibly be thin wrappers around std::vector and std::array.
EDIT: This avoid the magic constant...
template<typename T = void>
class Structure {};
template<typename T, std::size_t Size>
class Structure<T[Size]> {
T data[Size];
// The statically allocated implementation
};
template<typename T>
class Structure<T[]> {
T * pData;
public:
Structure(std::size_t size) : pData(new T[size]) {}
~Structure() { delete[] pData; }
// The dynamically allocated implementation
};
Instantiated like this...
Structure<int[]> heap(3);
Structure<int[3]> stack;
EDIT: Or use policies like so...
class AllocationPolicy {
protected:
static const std::size_t Size = 0;
};
template<std::size_t Size_>
class Static : AllocationPolicy {
protected:
static const std::size_t Size = Size_;
};
class Dynamic : AllocationPolicy {
protected:
static const std::size_t Size = 0;
};
template <typename T, typename TAllocationPolicy = Dynamic>
class Vector : TAllocationPolicy {
static_assert(!std::is_same<typename std::remove_cv<TAllocationPolicy>::type, AllocationPolicy>::value && std::is_base_of<AllocationPolicy, TAllocationPolicy>::value, "TAllocationPolicy must inherit from AllocationPolicy");
using TAllocationPolicy::Size;
public:
T data[Size];
};
template <typename T>
class Vector<T, Dynamic> : private Dynamic {
T * data;
public:
Vector(std::size_t size) : data(new T[size]) {}
~Vector() { delete [] data; }
};

Related

Member field aliases in derived class (no accessor functions)

Toy example:
template<typename T, std::size_t N>
class static_vector
{
public:
T& operator[](std::size_t i) { return m_elements[i]; }
T const& operator[](std::size_t i) const { return m_elements[i]; }
private:
std::array<T, N> m_elements;
};
template<typename T>
class vector3
: public static_vector<T, 3>
{
public:
using vector_type = static_vector<T, 3>;
// x = vector_type::operator[](0);
// y = vector_type::operator[](1);
// z = vector_type::operator[](2);
};
Let vector3<float> pos;. I want to access pos[0] via pos.x. Clearly, if pos is declared to be const, I want pos.x to be read-only.
Is this possible?
Let me stress the fact that I don't want to use accessor functions of the form
T& x() { return (*this)[0]; }
T const& x() const { return (*this)[0]; }
There is no zero cost way to do this using the exact syntax you want.
Relaxing either cost (compile, maintenance, memory usage and runtime) or syntax (which your () is an example of) gets you what you want.
I added a comment to your question but I thought I'd add an answer with some code to clarify. Be warned, what follows is not a good idea.
You can use simple pointer arithmetic to interpret members of a struct as if they were elements in an array. Because the type of the struct members and the type of elements in the pseudo array are the same, we're safe to reinterpret one as the other with the caveat that there is no padding in between the struct members.
The C++ standard gives no method for defining padding in a struct so you will have to rely on compiler specific directives. I believe however that both MSVC and GCC support #pragma pack.
#pragma pack(push, 1)
template <typename T>
struct Vec3
{
T x;
T y;
T z;
T& operator[](size_t i) { return *(&x + i); }
const T& operator[](size_t i) const { return *(&x + i); }
};
#pragma pack(pop)
So why isn't this a good solution?
You are relying on compiler specific directives making your code less portable.
You need to explicitly declare each member which means you'll need separate templates for Vec2, Vec3 and Vec4.
1 byte alignment isn't supported on all architectures again making your code less portable.
Even on architectures where unaligned memory access is supported (such as x86,) it comes with a performance penalty.

C++11, const data members, std::inserter, copy

There is a simple example of the class Test
#include <algorithm>
#include <iterator>
#include <vector>
template <typename T>
struct MinMax { T min, max; };
template <typename T>
using TList = std::vector<T>;
template <typename T>
class Test
{
private:
const T a, b;
const MinMax <T> m;
public:
Test() : a(0), m{ 0, 0 }, b(0.0) {};
public:
T getA() const { return a; }
MinMax <T> & getMinMax() const { return m; }
T getB() const { return b; }
Test(const Test &t) : a(t.a), b(t.b), m(t.m ) {}
};
with the constant data members. Instead of the constructor, the data are not changed. I would like to copy the vector of Test objects to another vector using std::inserter. I am surprised that the copy constructor is not sufficient
int main()
{
TList <Test <double> > t1;
TList <Test <double> > t2;
Test<double> t;
t1.push_back(t);
std::copy(t2.begin(), t2.end(), std::inserter(t1, t1.begin()));
return 0;
}
and the following compiler error appears (VS2015):
Error C2280 'Test<double> &Test<double>::operator =(const Test<double> &)': attempting to reference a deleted function Const
Is it possible to let the data members const and perform a copy in a different way (some hack :-))? Or an operator = must be defined, so the data members cannot be const (it is imppossible to assign to an object with const data members)?
Thanks for your help.
An insert to a vector reassigns all elements after the inserted element and assigns the inserted element to the freed up slot.
In other words, you can't because the Standard requires the elements of standard containers to be Asssignable (have a = b defined) to offer full functionality.
Apart from the obvious solution of writing your own operator= you can also add elements with const members to a vector by pushing back:
std::copy(t2.begin(), t2.end(), std::back_inserter(t1));
but that's kind of working against the Standard; push_back doesn't happen to need assignability but other functions may.
Or, you can use a container that doesn't require assignability to insert, e.g.:
template <typename T>
using TList = std::list<T>;
trading off all the benefits of contiguous memory cache locality of a vector.
On a closing note, I tend to avoid declaring data members of my structures const because of such problems with generic containers that require assignability under the hood. Notice that in your example removing const from private members would leave you with good-enough read-only fields (that can be accessed only via getters from outside).

How do you create a template definition for a fixed-size array class in C++?

I need to create a template because I don't know what it's an array of. And it needs to be of the size that's passed in the constructor. So here is what I got and I got all sorts of errors. I am a beginner to C++ so any help is appreciated :)
template <typename T, int N>
class Array
{
public:
T& operator[](int index)
{
return data[index];
}
private:
int size;
T *data[N];
};
I think you understand what I'm trying to do. I also need to overload the subscript operator, as you can see. Not sure if I need a reference or a pointer or what. I did have a constructor but it wasn't working properly.
Here's a corrected version with a sample main as well:
#include <iostream>
using namespace std;
template <typename T, int N>
class Array
{
public:
T& operator[](int index)
{
// add check for array index out of bounds i.e. access within 0 to N-1
return data[index];
}
Array() {
data = new T[size = N];
}
~Array() {
if (data)
delete [] data;
}
private:
int size;
T *data;
};
int main(void) {
Array<int, 4> a;
a[0] = 5;
cout << a[0] << endl;
return 0;
}
From what I can see your template is containing an array of pointers, which from what I read and from your [] operator implementation is not what you intend to do. So first you should remove the * from :
T *data[N];
You should probably initialize the size of your template from your constructor, thus you should change :
T *data[N];
to :
T* data;
and :
template <typename T, int N>
to :
template<typename T>
Now that changes the implementation a bit, you should now write a constructor like that :
template<typename T>
Array(int n) {
data = new T[n];
}
and now you should also add a destructor like that :
~Array() {
delete[] data;
}
and there you go :)
However if you want to keep the size as an argument of the template your constructor go like :
template<typename T, int N>
Array() {
}
and the declaration of data :
T data[N];
As said in comments, you may want to use std::array in "real life" conditions, but as a training, implementing your own Array is a good thing to do, that's a training most computer schools do after all.

using nested std::array to create an multidimensional array without knowing dimensions or extents until runtime

Is it possible for a class to have a member which is a multidimensional array whose dimensions and extents are not known until runtime?
I have found (via this guide) a way to create a struct to easily nest std::arrays at compile time using template metaprogramming:
#include <array>
/*
this struct allows for the creation of an n-dimensional array type
*/
template <typename T,size_t CurrentDimExtent,size_t... NextDimExtent>
struct MultiDimArray{
public:
//define the type name nestedType to be a recursive template definition.
using nestedType=typename MultiDimArray<T,NextDimExtent...>::type;
using type=std::array<nestedType,CurrentDimExtent>;
};
/*
This struct is the template specialization which handles the base case of the
final dimensional extent
*/
template <typename T,size_t DimExtent>
struct MultiDimArray<T,DimExtent>{
using type=std::array<T,DimExtent>;
};
this still falls short of satisfying my requirement in two ways (that I know of):
In order to declare a variable (or a pointer to a variable) of this type you must state the dimensions.
This only works when the DimExtents are constant expressions (set at compile time).
To demonstrate why number 2 is a distinct problem, here is a class with a set number of dimensions (2) using a void* to reference the multidimensional array:
template <typename T>
class TwoDimGrid{
public:
TwoDimGrid(const size_t extent1,const size_t extent2):
_twoDimArray(new MultiDimArray<T,extent1,extent2>);
private:
void* _twoDimArray;
};
This will not compile as extent1 and extent2 are not constant expressions.
other notes:
I would like to see if it's possible to accomplish using std:array, rather than native arrays or a dynamically resizing container like std::vector.
Please use smart pointers where appropriate (I didn't as I'm not really sure how to handle a smart void pointer).
Edit
I have fallen into the trap of The XY Problem with X being the first sentence of this question and Y being how to accomplish it with std::array. I therefore created a new question and am leaving this one here in case it's ever possible to solve Y problem.
old school multidimensional arrays, something along these lines:
template <typename T>
class multi
{
T*myArray;
size_t x_dim;
size_t y_dim;
public:
multi(size_t x, size t y) : x_dim(x), y_dim(y)
{
myArray = new T[x*y];
}
T& get(int x, int y)
{
return myArray[x*y_dim+y];
}
};
template <typename T>
class vvc
{
//possible ragged array ..non rigorous approach
//with management memory cost per element
//clearly not as efficient as .... linearized access where access index is
//row size * r + column
//memory management courtesy of vector
public:
std::vector< std::vector<T> > v;
};
int double_vector()
{
int x1 = 5;
int x2 = 3;
std::vector<int> r(x2);
vvc<int> vv;
int k = 0;
for (int i1 = 0; i1 < x1; ++i1)
{
for (int i2 = 0; i2 < x2; ++i2)
{
k += 1;
r[i2] = k;
}
vv.v.push_back(r);
}
//inspect
cout << vv.v[0][0] << " first " << endl;
for (auto const & t1 : vv.v)
{
for (auto const &t2 : t1 )
{
cout << t2 << " ";
}
cout << endl;
}
return 0;
}

Initialising nested templates

I'm trying to learn more about templates and have come across a problem I can't seem to solve. At the moment the class below works fine.
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
template <class T, int s>
class myArray{
public:
T* data;
inline T& operator[](const int i){return data[i];}
myArray(){
data=new T[s];
}
myArray(const myArray& other){
data=new T[s];
copy(other.data,other.data+s,data);
}
myArray& operator=(const myArray& other){
data=new T[s];
copy(other.data,other.data+s,data);
return *this;
}
~myArray(){delete [] data;}
};
If I use it:
myArray<myArray<myArray<int,10>,20>,30> a;
a is now 30x20x10 array that I can access with the normal array brackets e.g. a[5][5][5]. I wish to add a feature so that I could write:
myArray<myArray<myArray<int,10>,20>,30> a(10);
and initialise all of the entries to 10 for example. I can't work out how to do this. As I understand, each layer of myArray is constructed using the default constructor. If I changed this to something like:
myArray(int n=0){
data=new T[s];
fill(data,data+s,n); //T might not be of type int so this could fail.
}
I think this should fail when data is not of type int (i.e. on any array on dimensions > 1), however it doesn't. It works when the array is square, but if not then some of the entries aren't set to 10. Does anyone have an idea how the standard vectors class achieves this? Any help would be amazing. Thanks!
Well, try something like this:
myArray()
: data(new T[s]()) // value-initialization!
{
}
myArray(T const & val)
: data(new T[s]) // default-initialization suffices
{
std::fill(data, data + s, val);
}
If you're into variadic templates, you might cook up something even more grotesque involving variadically filled initializer lists, but I think we've done enough learning for one week.
Note the fundamental flaw in using new: Either version requires that your class T can be instantiated in some "default" state, and that it be assignable, even though we never require the default state in the second version. That's why "real" libraries separate memory allocation and object construction, and you never see a new expression unless its the placement version.
std::vector uses placement new on memory blocks. It constructs the data.after allocating the memory in a second line of code.
This technique would work for you as well. Be careful with placement new as it requires you to call destructors manually as well.
Here is a half-assed route without placement new:
template<typename U>
explicit MyArray( U const& constructFromAnythingElse )
{
AllocateSpace(N); // write this, just allocates space
for (int i = 0; i < N; ++i)
{
Element(i) = T( constructFromAnythingElse );
}
}
with placement new, you have to allocate the memory first, then construct in-place, and then remember to destroy each element at the end.
The above is half-assed compared to a placement new route, because we first construct each element, then build another one, and use operator= to overwrite it.
By making it a template constructor on an arbitrary type, we don't rely on multiple conversion to get multiple levels down into the array. The naive version (where you take a T const&) doesn't work because to construct an array of arrays of arrays of T, the outermost one expects an array of arrays of T as an argument, which expects an array of T as an argument, which expects a T -- there are too many levels of user defined construction going on there.
With the above template constructor, the array of array of array of T accepts any type as a constructor. As does the array of array of T, as does the array of T. Finally, the T is passed in whatever you constructed the outermost array of array of array of T, and if it doesn't like it, you get a compiler error message that is nearly completely unreadable.
Make specialization for arrays containing other arrays. To do this you need some common implementation class to be used in general and specialized MyArray:
Common implementation (I made some fixes for you - see !!! comments):
template <class T, int s>
class myArrayImpl {
public:
T* data;
T& operator[](int i){return data[i];} //!!! const before int not needed
const T& operator[](int i) const {return data[i];} //!!! was missing
myArrayImpl(){
data=new T[s]();
}
myArrayImpl(const myArrayImpl & other){
data=new T[s];
copy(other.data,other.data+s,data);
}
myArrayImpl& operator=(const myArrayImpl& other){
T* olddata = data; // !!! need to store old data
data=new T[s];
copy(other.data,other.data+s,data);
delete [] olddata; //!!! to delete it after copying
return *this;
}
~myArrayImpl(){delete [] data;}
};
Then make general implementation - note the definition of value_type and setAll:
template <class T, int s>
class myArray : private myArrayImpl<T,s> {
typedef myArrayImpl<T,s> Impl;
public:
using Impl::operator[];
myArray() : Impl() {}
typedef T value_type; // !!!
explicit myArray(const value_type& value) {
setAll(value);
}
void setAll(const value_type& value) {
fill(this->data, this->data + s, value);
}
};
And the specialized version for myArray of myArray - see also differences in value_type and setAll:
template <class T, int s1, int s2>
class myArray<myArray<T,s2>,s1> : private myArrayImpl<myArray<T,s2>,s1> {
typedef myArrayImpl<myArray<T,s2>,s1> Impl;
public:
using Impl::operator[];
myArray() : Impl() {}
typedef typename myArray<T,s2>::value_type value_type; // !!!
explicit myArray(const value_type& value) {
setAll(value);
}
void setAll(const value_type& value) {
for_each(this->data, this->data + s1, [value](myArray<T,s2>& v) { v.setAll(value); });
}
};
And usage:
int main() {
myArray<myArray<myArray<int,7>,8>,9> a(7);
std::cout << a[0][0][0] << std::endl;
std::cout << a[8][7][6] << std::endl;
}
Full example here: http://ideone.com/0wdT9D