简体   繁体   English

模板化功能模板专业化

[英]Templated function template specialization

I want to write a specialization for a template function, where the type for which it is specialized is itself a templated type. 我想为模板函数编写一个专门化,其中它专用的类型本身就是模板类型。 (I am using C++11 or higher.) (我使用的是C ++ 11或更高版本。)

In the example code below, I have the generic function convertTo and a working specialization for int , allowing me to use convertTo<int>(s) (as shown). 在下面的示例代码中,我有通用函数convertToint的工作特化,允许我使用convertTo<int>(s) (如图所示)。 But I cannot figure out how to write specializations for, eg, std::set<T> . 但我无法弄清楚如何编写专业化,例如std::set<T> This is what I tried: 这是我试过的:

#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;
}

With g++ 6.3.0 I get the error message: 使用g ++ 6.3.0,我收到错误消息:

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

So I tried to comment out the lines template<> above the attempted specializations, but then I get: 所以我试图在尝试的专业化之上注释掉行template<> ,但后来我得到:

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

I don't understand. 我不明白。 I didn't intend to write a partial specialization? 我不打算写一个局部专业化?

I do not want to use template<class Container> , because I want to be able to write specific code for different container classes. 我不想使用template<class Container> ,因为我希望能够为不同的容器类编写特定的代码。 (I need this for other template classes in my code.) (我的代码中的其他模板类需要这个。)

Any advise on how to do this? 有关如何做到这一点的任何建议?

For whatever reason a common answer for this problem is to forward to a static method of an implementation struct. 无论出于何种原因,此问题的常见答案是转发到实现结构的静态方法。 A struct can be partially specialized so this does solve the issue. 结构可以是部分专用的,所以这确实解决了这个问题。 But it's usually better to use overloading; 但通常使用重载更好; there are various reasons for that but here I'll simply restrict myself to saying that it's less boilerplate per implementation. 有各种各样的原因,但在这里我只是限制自己说每个实现的样板量较少。 You can do it like this: 你可以这样做:

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>{});
};

All of the convertTo taking two arguments are just overloads now, which is fine. 所有带有两个参数的convertTo现在只是重载,这很好。 The user facing convertTo simply calls to the two argument form using our little tag struct to control the dispatch in exactly the same way as partial specialization. 面向convertTo的用户只需使用我们的小标签结构调用两个参数形式,以与部分特化完全相同的方式控制调度。

There's lots of other interesting merits to this technique, like the ability to control overload resolution more precisely using additional tag -like structs and derived-base conversion, the utility of ADL/2-phase lookup, but it's a bit out of scope here. 这项技术有许多其他有趣的优点,比如使用额外的类似tag的结构和派生基础转换更精确地控制重载决策的能力,ADL / 2阶段查找的效用,但这里有点超出范围。

A partial specialization is when you specify part of the type of the template, but not the whole thing. 部分特化是指定模板类型的一部分,而不是整个事物。 For example, 例如,

template <>
template<class T> set<T> convertTo<set<T>>(const char* str)

would partially specialize for set<T> if partial function specialization were allowed. 如果允许部分函数专门化,将部分专门用于set<T>

The two main ways of handling this are to instead overload, by removing the template<> part, or better yet switch to using template class specialization. 处理此问题的两种主要方法是通过删除template<>部分来重载,或者更好地切换到使用模板类专门化。 The problem with overloading is that it looks somewhat different if you are overloading with another template (as with set<T> ) or with a single type (as with int ), and mixing specialization and overload almost certainly doesn't work as you expect. 重载的问题是,如果你用另一个模板(如set<T> )或单一类型(如int )重载它看起来有点不同,混合特化和重载几乎肯定不会像你期望的那样工作。

Therefore, template class specialization is usually the best way to go, and can be done like this: 因此,模板类专业化通常是最好的方法,可以这样做:

// 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);
}

That way you can use any type of specialization you want for the class without issue. 这样你就可以毫无问题地使用你想要的任何类型的专业化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM