模板化功能模板专业化

时间:2017-09-20 14:43:26

标签: c++ c++11 templates specialization

我想为模板函数编写一个专门化,其中专用的类型本身就是模板类型。 (我使用的是C ++ 11或更高版本。)

在下面的示例代码中,我有通用函数convertToint的工作特化,允许我使用convertTo<int>(s)(如图所示)。但我无法弄清楚如何编写专业化,例如std::set<T>。这就是我试过的:

#include <string>
#include <sstream>
#include <set>
#include <unordered_set>
using namespace std;

// generic version
template<class T> T convertTo(const char* str) {
  T output;
  stringstream ss(str, stringstream::in);
  ss >> output;
  return output;
}

// specialization for int. works.
template <>
int convertTo<int>(const char* str) {
  return atoi(str);
}

template <>
template<class T> set<T> convertTo<set<T>>(const char* str) {
  set<T> S;
  // TODO split str by comma, convertTo<T>(each element) and put into S
  return S;
}

template <>
template<class T> unordered_set<T> convertTo<unordered_set<T>>(const char* str) {
  unordered_set<T> S;
  // TODO split str by comma, convertTo<T>(each element) and put into S
  return S;
}

int main() {
  float f = convertTo<float>("3.141");
  int i = convertTo<int>("123");
  set<int> os = convertTo<set<int>>("9,8,7,6");
  unordered_set<int> os = convertTo<unordered_set<int>>("9,8,7,6");
  return 0;
}

使用g ++ 6.3.0,我收到错误消息:

 too many template parameter lists in declaration of ‘std::set<T> convertTo(const char*)’

所以我试图在尝试的专业化之上注释掉template<>行,但后来我得到了:

non-class, non-variable partial specialization ‘convertTo<std::set<T, std::less<_Key>, std::allocator<_CharT> > >’ is not allowed

我不明白。我没打算写一个部分专业化?

我不想使用template<class Container>,因为我希望能够为不同的容器类编写特定的代码。 (我的代码中的其他模板类需要这个。)

有关如何执行此操作的任何建议吗?

2 个答案:

答案 0 :(得分:4)

无论出于何种原因,此问题的常见答案是转发到实现结构的静态方法。结构可以是部分专用的,所以这确实解决了这个问题。但是使用重载通常会更好;有各种各样的原因,但在这里,我只是限制自己说它每个实现的样板量较少。你可以这样做:

template <class T>
struct tag{}; // implementation detail

template<class T>
T convertTo(const char* str, tag<T>) {
  T output;
  stringstream ss(str, stringstream::in);
  ss >> output;
  return output;
}

int convertTo(const char* str, tag<int>) {
  return atoi(str);
}

template<class T>
set<T> convertTo(const char* str, tag<set<T>>) {
  set<T> S;
  // TODO split str by comma, convertTo<T>(each element) and put into S
  return S;
}

template<class T>
unordered_set<T> convertTo(const char* str, tag<unordered_set<T>>) {
  unordered_set<T> S;
  // TODO split str by comma, convertTo<T>(each element) and put into S
  return S;
}

template <class T>
T convertTo(const char * s) {
    return convertTo(s, tag<T>{});
};

所有带有两个参数的convertTo现在只是重载,这很好。面向convertTo的用户只需使用我们的小标签结构调用两个参数形式,以与部分特化完全相同的方式控制调度。

这项技术有许多其他有趣的优点,比如使用额外的tag更精确地控制重载分辨率的能力 - 比如结构和派生基转换,ADL / 2阶段的效用查找,但它在这里有点超出范围。

答案 1 :(得分:2)

部分特化是指定模板类型的一部分,而不是整个事物。例如,

template <>
template<class T> set<T> convertTo<set<T>>(const char* str)
如果允许部分函数特化,

将部分专门用于set<T>

处理此问题的两种主要方法是通过删除template<>部分来重载,或者更好地切换到使用模板类专门化。重载的问题是,如果你使用另一个模板(如set<T>)或单一类型(与int一样)重载,它看起来有些不同,并且混合特化和重载几乎肯定没有按照你的期望工作。

因此,模板类专业化通常是最好的方法,可以这样做:

// Generic version
template <typename T>
class Converter {
  public:
    T operator() (const char* str) {
          T output;
          stringstream ss(str, stringstream::in);
          ss >> output;
          return output;
    }
};

template <>
class Converter<int> {
  public:
    int operator() (const char* str) {
        return atoi(str);
    }
};

// ...

template <typename T>
T convertTo(const char* str) {
    return Converter<T>{}(str);
}

通过这种方式,您可以毫无问题地使用您想要的任何类型的专业化。