【问题标题】:Initialising nested templates初始化嵌套模板
【发布时间】:2012-11-05 16:36:22
【问题描述】:

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

#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 类型(即在维度 > 1 的任何数组上)时,这应该会失败,但事实并非如此。它在数组为正方形时有效,但如果不是,则某些条目未设置为 10。有谁知道标准向量类如何实现这一点?任何帮助都会很棒。谢谢!

【问题讨论】:

  • 你的赋值运算符没有清除旧数据。

标签: c++ templates constructor


【解决方案1】:

好吧,试试这样的:

 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 表达式。

【讨论】:

  • 谢谢。我这样做了,当“MyArrays”的维度为 2 或更少时它可以工作。对于较大的数组,我得到'没有匹配函数调用'myArray, 3>, 3>::myArray(int)''。如果有人有机会看一眼,这是我的新代码:http://ideone.com/PxJgo6 谢谢!
【解决方案2】:

std::vector 在内存块上使用放置 new。它在第二行代码中分配内存后构造数据。

这项技术也适用于您。放置 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&)不起作用,因为要构造一个由 T 组成的数组的数组,最外面的一个期望一个由 T 组成的数组作为参数,它期望一个由 T 组成的数组作为参数,它需要一个 T - 那里有太多级别的用户定义的构造。

使用上面的模板构造函数,T 数组的数组可以接受任何类型的构造函数。与 T 的数组的数组一样,T 的数组也是如此。最后,无论您构造了 T 的数组的最外层数组,都将 T 传递给它,如果它不喜欢它,则会出现编译器错误几乎完全无法阅读的消息。

【讨论】:

    【解决方案3】:

    对包含其他数组的数组进行特化。为此,您需要一些通用的实现类来通用和专门的 MyArray:

    通用实现(我为您做了一些修复 - 请参阅 !!! cmets):

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

    以及 myArray 的 myArray 的专用版本 - 另请参阅 value_type 和 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); });
        }
    };  
    

    及用法:

    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

    【讨论】:

    • 为什么不用 template 显式 myArray(U const & u)?即,接受任何类型,如果它不能用于构造内容,则会得到错误。实际上,我们想说的是“任何可以用来构建我的内容的东西对我来说都是公平的游戏”——只要你的内容是 myArrays,你的就可以工作。
    • @Yakk 你可能是对的 - 我没有检查 - 但它似乎是合理的......我只是担心它会太笼统。此外 - 任何可转换为 value_type 的类型都可以在我的解决方案中使用 - 而且这种转换只会进行一次 - 不是针对 [multi]arrays 中的每个单元格。
    • 这是您解决方案的一个不错的功能! +1
    猜你喜欢
    • 2016-08-08
    • 2016-04-12
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-08
    • 1970-01-01
    相关资源
    最近更新 更多