多种类型的类模板专业化

时间:2019-03-29 15:44:31

标签: c++ c++11 templates

我发现了一些类似的问题,但找不到针对我的特殊情况的直接答案。 模板的整个语法令我感到困惑,因此我可能会误解了一些东西。

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

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和float变体,而不是只为int类型编写这种特化。我找到了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;
}

2 个答案:

答案 0 :(得分:5)

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

df$mpg_rating <- with(df, c("bad", "ok", "good")[findInterval(mpg, c(20, 30))+1])

live example here

这里的窍门在于使用第二个模板参数(可以不命名,并使用默认参数)。当您使用专业化的类模板时,例如#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>int(之所以存在,是因为主模板的第二个参数具有默认参数) 。然后,我们尝试推论我们唯一的局部专业化的论点

void

来自模板参数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未被定义并且替换失败。由于substituion failure is not an error在这种情况下,这仅意味着部分专业化不是此处的选项,而是将使用主模板。

如果存在多个匹配的局部专业,则必须对它们进行排名以选择最佳匹配。 The actual process相当复杂,但通常归结为选择最具体的专业化。

答案 1 :(得分:2)

虽然像这样的一个小示例,可以对整个类进行专业化处理,但在更复杂的情况下,您可能希望避免重复所有成员,以便可以将一个成员添加到专业化处理中。为此,一种常见的技术是从公共基类继承额外的成员函数,并且仅专门使基类具有或不具有成员。您必须使用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;
};

Coliru link