不同的功能取决于模板类中的类型

Different functionality depending on type in templated classes

本文关键字:类型 取决于 功能      更新时间:2023-10-16

我本来打算为Array样式的类使用一个模板,然后只传递一个char*int,但在尝试实现以下内容时遇到了问题:

template<Typename T>
class Array {
    void add(T elem) {
        if(size == capacity) expandArr();
        if(T == char*) //g++ threw errors here
            arr[size] = new char[strlen(word) + 1];
            strcpy(arr[size], word);
        else if(T == int) {
            arr[size] = elem;
        }
        size++;
    }
}

是否有其他合法检查元素类型的方法?还是我应该把这两个班分开,让它们独立?

我最初尝试使用模板化类,因为两种Array类型具有相同的功能,但在这种情况下,char*int的内存分配方式存在一些根本差异。

在这种情况下,类模板专业化有些过头了——它迫使您为不同的类型复制整个类,即使大约90%的功能是相同的。

关键是要将那些不同的部分隔离开来,只针对那些部分。不过,由于几个原因,使用重载比专业化更容易。

在您的案例中,不同的部分只是将值分配给数组项。

template <typename T>
class Array {
    void add(T elem) {
        if(size == capacity) expandArr();
        assignItem(elem);
        size++;
    }
    template <typename U>
    void assignItem(U elem) {
        arr[size] = elem;
    }
    void assignItem(char* elem) {
        // Incidentally, DON’T DO THIS! It leaks. Use RAII!
        arr[size] = new char[strlen(elem) + 1];
        strcpy(arr[size], word);
    }
};

当然,现在您已经对支持的Array类型进行了硬编码:只支持具有复制构造函数char*的类型,不支持其他类型。通常情况下,您不需要此限制,您希望支持任意类型,并允许它们指定如何复制1

实现这一点有几种方法。我将在这里通过一个指定如何复制类型的trait类来说明一个。

template <typename T>
struct CopyConstruct {
    void assign(T& target, T source) const {
        target = source;
    }
};
template <typename T, typename Copy = CopyConstruct<T>>
class Array {
    Copy copier;
    void add(T elem) {
        if(size == capacity) expandArr();
        copier.assign(arr[size], elem);
        size++;
    }
};

这个类可以与初始类使用相同。对于char*,用户有两种选择:要么专门化CopyConstruct,要么在实例化Array时提供完全自己的特性,如下所示:

struct CopyCString {
    void assign(char*& target, char* source) const {
        target = new char[strlen(elem) + 1];
        strcpy(arr[size], word);
    }
};
// And then:
Array<char*, CopyCString> strArray;

(请注意,我们将引用传递给此处的指针,否则分配的内存将丢失,因为我们将指针分配给数组项的副本,而不是项本身。)


1但实际上C++已经使用了复制构造函数来达到这个目的真正的解决方案不是上述任何一种,而是将char*包装成一个可复制的类型,如std::string

您应该使用模板专业化来为不同的模板类型提供不同的行为。

将您的代码更改为:

template<typename T>
class Array {
    void add(T elem) {
        if(size == capacity) expandArr();
        size++;
    }
}
template<>
class Array<char*> {
    void add(T elem) {
        if(size == capacity) expandArr();
        arr[size] = new char[strlen(word) + 1];
        strcpy(arr[size], word);
        size++;
    }
}
template<>
class Array<int> {
    void add(T elem) {
        if(size == capacity) expandArr();
        arr[size] = elem;
        size++;
    }
}