所以我有一个相当复杂的函数:
template <typename T>
void foo(const int param1, const int param2, int& out_param)
在给定int bar
、const int arg1
和const int arg2
的情况下,将使用foo<plus<int>>(arg1, arg2, bar)
或foo<minus<int>>(arg1, arg2, bar)
调用函数
在内部,该函数相当复杂,但我根据作为模板参数传递的函数器的类型,执行不同的关系运算符。
对于plus
,我需要做的是:
arg1 > arg2
bar > 0
bar > -10
对于minus
,我需要做的是:
arg1 < arg2
bar < 0
bar < 10
请注意,10
在两个3中没有相同的符号。我目前正在通过传递第二个模板参数(less
或greater
)来解决所有这些问题。但我在想,将这些关系写成算术运算可能更有意义。这有可能吗,或者我需要使用第二个模板参数吗?
发布于 2018-12-13 04:10:40
T{}(0, arg1) > T{}(0,arg2);
T{}(0, bar) > 0;
T{}(0, bar) > -10;
基本思想是a > b
当且仅当-a < -b
。和plus(0,a)==a
,同时使用minus(0,a)==-a
。
最后一个比较棘手,因为我们想要改变<
和符号的顺序。幸运的是他们取消了:
假设我们想要一个常量,在正的情况下是-10
,在负的情况下是10
。然后
plus(0,-10)
是-10
和
minus(0,-10)
是10
。
所以我们得到:
T{}(0, bar) > T{}(0, T{}(0,-10))
在加号的情况下,rhs是0+0+-10
,又名-10
。
在负的情况下,这是0-(0-(-10))
,也就是-10
。
所以简短的形式是:
T{}(0,bar) > -10
它应该是有效的。
发布于 2018-12-14 00:45:43
除了@Yakk's answer之外,还有许多方法可以做到这一点。这里有5个。
方法1:功能特性
在更高级的模板元编程技术出现之前,这是一种更经典的技术。它仍然非常方便。我们特化了一些依赖于T
的结构,以提供我们想要使用的类型和常量。
template<class T>
struct FooTraits;
template<class T>
struct FooTraits<std::plus<T>>
{
using Compare = std::greater<T>;
static constexpr std::tuple<int, int> barVals{0, 10};
};
template<class T>
struct FooTraits<std::minus<T>>
{
using Compare = std::less<T>;
static constexpr std::tuple<int, int> barVals{0, -10};
};
template <class T>
void foo(const int arg1, const int arg2, int& bar)
{
using traits = FooTraits<T>;
typename traits::Compare cmp{};
cmp(arg1, arg2);
cmp(bar, std::get<0>(traits::barVals));
cmp(bar, std::get<1>(traits::barVals));
}
方法2:完全专业化
另一种仍然有用的“经典”技术。您可能对此技术很熟悉,但为了完整起见,我将展示它。只要你永远不需要部分专门化一个函数,你就可以为你需要的类型编写不同版本的函数:
template <class T>
void foo(const int arg1, const int arg2, int& bar);
template <>
void foo<std::plus<int>>(const int arg1, const int arg2, int& bar)
{
arg1 > arg2;
bar > 0;
bar > 10;
}
template <>
void foo<std::minus<int>>(const int arg1, const int arg2, int& bar)
{
arg1 < arg2;
bar < 0;
bar < -10;
}
方法3:标记派单
第三种将类型检查转变为重载问题的经典技术。要点是,我们定义了一些可以实例化的轻量级tag
结构,然后将其用作重载之间的区别。通常,当你有一个模板化的类函数,并且你不想专门化整个类来专门化所说的函数时,这是很好用的。
namespace detail
{
template<class...> struct tag{};
void foo(const int arg1, const int arg2, int& bar, tag<std::plus<int>>)
{
arg1 > arg2;
bar > 0;
bar > 10;
}
void foo(const int arg1, const int arg2, int& bar, tag<std::minus<int>>)
{
arg1 < arg2;
bar < 0;
bar < -10;
}
}
template <class T>
void foo(const int arg1, const int arg2, int& bar)
{
return detail::foo(arg1, arg2, bar, detail::tag<T>{});
}
方法4:简单的constexpr if
由于C++17,我们可以使用if constexpr
块对类型进行编译时检查。这很有用,因为如果检查失败,编译器根本不会编译该块。这通常会导致代码比以前容易得多,在以前,我们必须使用复杂的间接指向具有高级元编程的类或函数:
template <class T>
void foo(const int arg1, const int arg2, int& bar)
{
if constexpr (std::is_same_v<T, std::plus<int>>)
{
arg1 > arg2;
bar > 0;
bar > 10;
}
if constexpr(std::is_same_v<T, std::minus<int>>)
{
arg1 < arg2;
bar < 0;
bar < -10;
}
}
方法5:constexpr
+ trampolining
trampolining是一种元编程技术,您可以使用"trampoline“函数作为调用者和您希望调度到的实际函数之间的中介。在这里,我们将使用它来映射到适当的比较类型(std::greater
或std::less
)以及我们希望比较bar
的整数常量。它比方法4更灵活,也将关注点分离了一点。以可读性为代价:
namespace detail
{
template<class Cmp, int first, int second>
void foo(const int arg1, const int arg2, int& bar)
{
Cmp cmp{};
cmp(arg1, arg2);
cmp(bar, first);
cmp(bar, second);
}
}
template <class T>
void foo(const int arg1, const int arg2, int& bar)
{
if constexpr (std::is_same_v<T, std::plus<int>>)
return detail::foo<std::greater<int>, 0, 10>(arg1, arg2, bar);
if constexpr(std::is_same_v<T, std::minus<int>>)
return detail::foo<std::less<int>, 0, -10>(arg1, arg2, bar);
}
https://stackoverflow.com/questions/53750460
复制相似问题