多种类型的类模板专用化

Class Template specialization for multiple types

本文关键字:专用 种类 类型      更新时间:2023-10-16

我发现了一些问题,这些问题提出了类似的东西,但无法为我的特定情况找到直接的答案。 模板的整个语法对我来说非常混乱,所以我可能只是误解了一些东西。

我有一个应该接受每种类型的类模板。 简单的例子:

template <class T>
class State {
public:
void set(T newState);
T get();
private:
T state;
};
template <class T>
void State<T>::set(T newState){
state = newState;
}
template <class T>
T State<T>::get(){
return state;
}

现在,我想为一组类型提供一个专门的模板,为这些类型添加一个附加函数。从我目前发现的情况来看,我可以利用所谓的type_traits但它们究竟是如何用来实现这一目标的,这对我来说仍然是一个谜。

F.e. 这种针对 int 类型的专用化,但不是只为 int 类型编写这个,我还想允许所有其他 int 和 float 变体。我找到了 std::is_arithmetic,但不知道如何利用它来实现这一目标。

template <>
class State <int> {
public:
void set(int newState);
int get();
int multiplyState(int n);
private:
int state;
};
void State<int>::set(int newState){
state = newState;
}
int State<int>::get(){
return state;
}
int State<int>::multiplyState(int n){
return state*n;
}

您可以将部分模板专用化与 SFINAE 结合使用来实现此目的:

#include <type_traits>
template <class T, typename = void>
class State
{
T state;
public:
void set(T newState)
{
state = newState;
}
T get()
{
return state;
}
};
template <typename T>
class State<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{
T state;
public:
void set(int newState)
{
state = newState;
}
int get()
{
return state;
}
int multiplyState(int n)
{
return state*n;
}
};

现场示例在这里

这里的诀窍在于使用第二个模板参数(可以未命名并被赋予默认参数)。当你使用类模板的专用化时,例如,State<some_type>,编译器必须弄清楚应该使用哪个模板。为此,它必须以某种方式将给定的模板参数与每个模板进行比较,并确定哪个是最佳匹配的。

这种匹配的实际方式是尝试从给定的模板参数中推断出每个部分专用化的参数。例如,在State<int>的情况下,模板参数将被intvoid(后者的存在是因为主模板的第二个参数的默认参数)。然后,我们试图推导出我们唯一的部分专业化的论据

template <typename T>
class State<T, std::enable_if_t<std::is_arithmetic_v<T>>>;

从模板参数int, void.我们的偏特化有一个单参数T,可以直接从第一个模板int参数推导出来。有了这个,我们已经完成了,因为我们已经推导出了所有参数(这里只有一个)。现在我们将推导的参数代入部分专用化:State<T, std::enable_if_t<std::is_arithmetic_v<T>>>。我们最终得到State<int, void>,它匹配int, void的初始参数列表。因此,部分模板专用化适用。

现在,如果我们写了State<some_type>,其中some_type不是算术类型,那么直到我们成功推导出要some_type的部分专业化的参数为止,该过程将是相同的。同样,我们将参数代入部分专用化State<T, std::enable_if_t<std::is_arithmetic_v<T>>>。但是,std::is_arithmetic_v<some_type>现在将被false,这将导致std::enable_if_t<…>未被定义并且替换失败。由于替换失败在此上下文中不是错误,因此这仅意味着此处不选择部分专用化,而是使用主模板。

如果有多个匹配的部分专业化,则必须对它们进行排名以选择最佳匹配。实际过程相当复杂,但通常归结为选择最具体的专业化。

虽然对于这样的小示例,可以专门化整个类,但在更复杂的情况下,您可能有兴趣避免复制所有成员,以便您可以将一个成员添加到专业化中。为此,一种常见的技术是从公共基类继承额外的成员函数,并且仅将基类专用化为具有或没有成员。必须使用 CRTP,以便基类成员函数知道如何访问派生类。这看起来像:

// StateBase only contains the extra multiplyState member when State tells it to
// define it, based on T being an arithmetic type
template <class D, class T, bool has_multiply>
struct StateBase {};
template <class D, class T>
struct StateBase<D, T, true> {
T multiplyState(int n) {
return static_cast<D*>(this)->state * n;
}
};
template <class T>
class State : public StateBase<State<T>, T, std::is_arithmetic<T>::value> {
public:
// no need to duplicate these declarations and definitions
void set(T newState);
T get();
private:
// note that we write State::StateBase to force the injected-class-name to be found
friend struct State::StateBase;
T state;
};

科里鲁链接