我遇到了需要一个具有相同输出的模板函数的问题,只要它的模板参数是相同的,与位置无关。鉴于总有两个参数。
我有一个功能:
template<typename Lhs, typename Rhs>
int func();
我希望func<int, float>()
和func<float, int>()
调用相同的代码。
我想到了一个我想避免使用的宏,但是当两种类型相同时我不需要重复代码。所以宏如:
#define DEF_TEMPL_IMPL(lhs, rhs, ret) \
template<>\
auto func<lhs, rhs>(){ return ret; }\
template<>\
auto func<rhs, lhs>(){ return func<lhs, rhs>(); }
将无法编译,因为DEF_TEMPL_IMPL(float, float, 3)
会导致重新定义func<>
我认为SFINAE就是答案,但不能想到解决方案。
我会继续思考这个问题,但是在回答这个问题之前,堆栈溢出的一些优秀思想可能会比我能提出的更好或更优雅。
那怎么能实现呢?
答案 0 :(得分:9)
您可以为每个类型对编写一个专门化,然后在调用它时将主模板委托给func<Rhs,Lhs>()
:
//This will get called if no specializations match
template<typename Lhs, typename Rhs>
int func() {
//Check if there is a specialization for the flipped pair
return func<Rhs, Lhs>();
}
//This will get called for <int,float> and <float,int>
template <>
int func<int,float>() {
return 42;
}
//Ditto for <bool,float> and <float,bool>
template <>
int func<bool,float>() {
return 0xbeef;
}
//Specializations with the same arguments are also supported by this scheme
template <>
int func<bool,bool>() {
return 12;
}
答案 1 :(得分:3)
这是另一种可能的解决方案
template<typename A, typename B>
struct List { };
template<typename A, typename B>
struct Set {
Set(List<A, B>) { }
Set(List<B, A>) { }
};
template<typename A>
struct Set<A, A> {
Set(List<A, A>) { }
};
现在,将每个写为Set<A, B>
的重载,就像这样
template<typename Lhs, typename Rhs>
int func() {
return func(List<Lhs, Rhs>());
}
int func(Set<int, float>) {
return 42;
}
int func(Set<bool, float>) {
return 0xbeef;
}
int func(Set<bool, bool>) {
return 12;
}
这样,您还可以定义func(List<float, int>)
,如果由于某种原因,float, int
的顺序很重要。如果您不小心定义了Set<int,float>
和Set<float, int>
,则调用将是不明确的。
您还可以通过更改二进制文件Set<A, B>
的定义,在定义重载时验证您没有重复Set<B, A>
vs Set
的重载。到这个
template<typename A, typename B>
struct Set {
Set(List<A, B>) { }
Set(List<B, A>) { }
friend void dupeCheck(List<A, B>) { }
friend void dupeCheck(List<B, A>) { }
};
然而,对于这个技巧,这并不是绝对必要的。特别是因为JIT编译器中的类型可以以一致的方式轻松地手动排序。
答案 2 :(得分:1)
您可以定义类型的顺序(例如int
- 1,float
- 2,char
- 3)。
然后实现一个将这两种类型排序的函数:
#include <iostream>
template<typename T>
struct Order;
// There should be a better solution for getting type id in compile time
// This will let you specify the order
template<>
struct Order<int>
{
enum { Value = 1 };
};
template<>
struct Order<float>
{
enum { Value = 2 };
};
template<>
struct Order<char>
{
enum { Value = 3 };
};
template<typename A, typename B, bool swap>
struct Sort;
template<typename A, typename B>
struct Sort<A, B, false>
{
typedef A First;
typedef B Second;
};
template<typename A, typename B>
struct Sort<A, B, true>
{
typedef B First;
typedef A Second;
};
template<typename Lhs, typename Rhs>
int func_sorted()
{
return 1;
}
template<>
int func_sorted<int, float>()
{
return 2;
}
template<>
int func_sorted<float, int>()
{
return 3;
}
template<typename Lhs, typename Rhs>
int func()
{
typedef typename Sort<
Lhs,
Rhs,
((int)Order<Lhs>::Value > (int)Order<Rhs>::Value)>::First First;
typedef typename Sort<
Lhs,
Rhs,
((int)Order<Lhs>::Value > (int)Order<Rhs>::Value)>::Second Second;
return func_sorted<First, Second>();
}
所以关注代码
int main()
{
std::cout << func<int, float>() << std::endl;
std::cout << func<float, int>() << std::endl;
}
将打印:
2
2