初始化嵌套模板

时间:2012-11-05 16:36:22

标签: c++ templates constructor

我正在尝试更多地了解模板,并遇到了一个我似乎无法解决的问题。目前,下面的课程工作正常。

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

如果我使用它:

myArray<myArray<myArray<int,10>,20>,30> a;

a现在是30x20x10阵列,我可以使用普通数组括号访问它,例如一个[5] [5] [5]。我希望添加一个功能,以便我可以写:

myArray<myArray<myArray<int,10>,20>,30> a(10);
例如,

并将所有条目初始化为10。我无法弄清楚如何做到这一点。据我所知,myArray的每一层都是使用默认构造函数构造的。如果我将其更改为:

myArray(int n=0){
        data=new T[s]; 
        fill(data,data+s,n); //T might not be of type int so this could fail.
}

我认为当数据不是int类型时(即在尺寸&gt; 1上的任何数组上),这应该会失败,但事实并非如此。当数组是正方形时它可以工作,但如果没有,那么一些条目不会设置为10.有没有人知道标准向量类如何实现这一点?任何帮助都会很棒。谢谢!

3 个答案:

答案 0 :(得分:3)

好吧,尝试这样的事情:

 myArray()
 : data(new T[s]())      // value-initialization!
 {
 }

 myArray(T const & val)
 : data(new T[s])        // default-initialization suffices
 {
     std::fill(data, data + s, val);
 }

如果您使用的是可变参数模板,那么您可能会做一些更奇怪的事情,包括可变填充的初始化列表,但我认为我们已经做了足够的学习一周。

请注意使用new时的基本缺陷:任何一个版本都要求您的类T可以在某些“默认”状态下实例化,它可以分配,甚至虽然我们从不要求第二个版本中的默认状态。这就是“真正的”库分离内存分配和对象构造的原因,除非它的位置版本,否则你永远不会看到new表达式。

答案 1 :(得分:1)

std :: vector在内存块上使用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 );
  }
}

使用placement new,你必须首先分配内存,然后就地构造,然后记住在最后销毁每个元素。

与放置新路线相比,上述内容是半成品,因为我们首先构造每个元素,然后构建另一个元素,并使用operator=覆盖它。

通过使其成为任意类型的模板构造函数,我们不依赖于多次转换来将多个级别放入数组中。天真的版本(你采用T const&amp;)不起作用,因为要构造一个T数组的数组数组,最外层的数组需要一个T数组的数组作为参数,它需要一个T数组作为参数,它期望一个T - 那里有太多的用户定义构造级别。

使用上面的模板构造函数,T数组的数组数组接受任何类型作为构造函数。就像T的数组数组一样,T的数组也是如此。最后,无论你构造T的数组最外层数组,都会传入T,如果它不喜欢它,你会得到一个编译器错误消息几乎完全不可读。

答案 2 :(得分:1)

对包含其他数组的数组进行专门化。要做到这一点,你需要一些通用的实现类来使用一般和专门的MyArray:

常见实施(我为你做了一些修复 - 见!!!评论):

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

然后进行一般性实施 - 请注意value_typesetAll的定义:

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

myArray的myArray专用版 - 请参阅value_typesetAll的差异:

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

用法:

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

此处的完整示例:http://ideone.com/0wdT9D