模板化函数模板特化
Templated function template specialization
我想为模板函数编写专门化,其中专门化的类型本身就是模板化类型。 (我使用的是 C++11 或更高版本。)
在下面的示例代码中,我有通用函数 convertTo
和 int
的工作特化,允许我使用 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>
,因为我希望能够为不同的容器编写特定的代码类。 (我的代码中的其他模板 类 需要这个。)
关于如何做到这一点有什么建议吗?
部分特化是指您指定模板类型的一部分,而不是全部。例如,
template <>
template<class T> set<T> convertTo<set<T>>(const char* str)
如果允许部分函数专门化,将部分专门化 set<T>
。
处理这个问题的两种主要方法是通过删除 template<>
部分来代替重载,或者更好的是切换到使用模板 class 专业化。重载的问题在于,如果您使用另一个模板(如 set<T>
)或单一类型(如 int
)重载,它看起来会有些不同,并且混合专业化和重载几乎肯定不会没有如你所愿。
因此,模板 class 专业化通常是最好的方法,可以这样做:
// 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);
}
这样您就可以毫无问题地为 class 使用您想要的任何类型的专业化。
无论出于何种原因,此问题的常见答案是转发到实现结构的静态方法。一个结构可以部分特化,所以这确实解决了这个问题。但通常使用重载会更好;这有多种原因,但在这里我只想说每个实现的样板文件更少。你可以这样做:
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-phase 查找的实用程序,但有点超出范围。
我想为模板函数编写专门化,其中专门化的类型本身就是模板化类型。 (我使用的是 C++11 或更高版本。)
在下面的示例代码中,我有通用函数 convertTo
和 int
的工作特化,允许我使用 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>
,因为我希望能够为不同的容器编写特定的代码类。 (我的代码中的其他模板 类 需要这个。)
关于如何做到这一点有什么建议吗?
部分特化是指您指定模板类型的一部分,而不是全部。例如,
template <>
template<class T> set<T> convertTo<set<T>>(const char* str)
如果允许部分函数专门化,将部分专门化 set<T>
。
处理这个问题的两种主要方法是通过删除 template<>
部分来代替重载,或者更好的是切换到使用模板 class 专业化。重载的问题在于,如果您使用另一个模板(如 set<T>
)或单一类型(如 int
)重载,它看起来会有些不同,并且混合专业化和重载几乎肯定不会没有如你所愿。
因此,模板 class 专业化通常是最好的方法,可以这样做:
// 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);
}
这样您就可以毫无问题地为 class 使用您想要的任何类型的专业化。
无论出于何种原因,此问题的常见答案是转发到实现结构的静态方法。一个结构可以部分特化,所以这确实解决了这个问题。但通常使用重载会更好;这有多种原因,但在这里我只想说每个实现的样板文件更少。你可以这样做:
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-phase 查找的实用程序,但有点超出范围。