【问题标题】:Templated function template specialization模板化函数模板特化
【发布时间】:2017-09-20 14:43:25
【问题描述】:

我想为模板函数编写一个特化,其中特化的类型本身就是一个模板化类型。 (我使用的是 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&lt;&gt; 行,但后来我得到:

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

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

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

有什么建议吗?

【问题讨论】:

  • 函数模板不能部分特化。而是重载。
  • “我不打算专门化” - 然而你使用了专门化语法convertTo&lt;set&lt;T&gt;&gt;
  • 这肯定是骗人的,SO 上有数百万个部分专业化的函数模板。
  • 我确实打算专门化,但据我了解,“部分模板专门化”意味着只修复一些模板参数,而将其他模板化。所以,我想我想写一个完整的专业? @n.m.:我认为这就是我通过删除 template&lt;&gt; 所尝试的。
  • @lawilog 虽然还没有完全修复,set&lt;T&gt; 仍然有一个免费参数。部分专业化不仅是固定一个模板参数,同时保留第二个模板参数。您可以只使用一个模板参数来部分特化一个结构;如果您有T 的主要定义和set&lt;T&gt; 的次要定义,则第二个定义仍然是模板(因此,不是完全专业化的),但在某些情况下它比主要定义更专业。因此,部分专业化。

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


【解决方案1】:

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

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 阶段查找的实用程序,但它有点超出范围。

【讨论】:

  • 谢谢。看起来不错。在这种情况下,我将其命名为 convert(x, to); :-)
  • 这是另一个很好的解决方案。我更喜欢我提供的解决方案,因为它不涉及添加虚拟参数,但如果类型自然出现在函数参数中,我会使用重载。但是请注意,如果您将专业化添加到组合中,可能会发生奇怪的事情。
【解决方案2】:

部分特化是指您指定模板类型的一部分,而不是全部。例如,

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

如果允许部分函数特化,将部分特化set&lt;T&gt;

处理这个问题的两种主要方法是通过删除template&lt;&gt; 部分来代替重载,或者更好地切换到使用模板类专业化。重载的问题在于,如果您使用另一个模板(如set&lt;T&gt;)或单一类型(如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);
}

这样你就可以毫无问题地使用你想要的任何类型的专业化课程。

【讨论】:

  • 非常不同意这一点,由于各种原因,重载是优越的,其中一些只是有时相关,但如果没有其他原因,它是每个部分专业化的样板。
  • 谢谢。这就是我为我填写// ... 的方式: template struct Converter> { inline set operator() (const char* str) { set S; /* 待办事项 */ return S; } };
  • @NirFriedman 当类型根本不出现在参数中时我更喜欢这种方法,因为我不喜欢虚拟参数,但这也是一个非常好的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多