如果未实施除法运算符,则 SFINAE 回退
SFINAE fallback if division operator is not implemented
我想编写一个函数,它在两个不同类型的参数 a
和 b
之间执行除法,如果定义了除法运算符,则使用表达式 a/b
,否则如果没有这样的运算符,返回 a * (1/b)
。
这是我的想法,但我不知道如何在定义了 *
和 /
运算符时禁用第二个定义(或优先考虑第一个定义):
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (a/b) {
return a/b;
}
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (a * (U(1)/b)) {
return a * (U(1)/b);
}
有点难看,但在 gcc 5.2.0 c++14 下对我有用:
template<typename T, typename U, class R = int>
struct smart_division_helper {
auto operator() (T a, U b) -> decltype (a * (U(1)/b)) {
return a*(U(1)/b);
}
};
template<typename T, typename U>
struct smart_division_helper<T, U, decltype(declval<T>()/declval<U>(), 1)> {
auto operator() (T a, U b) -> decltype (a/b) {
return a/b;
}
};
template<class T, class U>
auto smart_division(T a, U b) -> decltype (smart_division_helper<T,U,void>()(a,b)) {
return smart_division_helper<T,U,int>()(a,b);
}
重点是让一个比另一个更专业。所以我们需要偏特化,因此需要 helper class (仿函数)。在此之后,我们有一个使用乘法的通用 class 和一个使用除法的专用 class,但前提是它被允许。
decltype(something, 1)
计算结果为 int
,但前提是 something
正确。
我相信这可以做得更容易。
最简单的技巧是依赖已经定义了优先级规则的重载决策。在下面的解决方案中,附加参数 0
,0 -> int
优于 0 -> char
,因此,如果不被表达式 SFINAE 排除,前者将是首选,而后者仍然可用于回退调用。
#include <utility>
template <typename T, typename U>
auto smart_division_impl(T a, U b, int)
-> decltype(a/b)
{
return a/b;
}
template <typename T, typename U>
auto smart_division_impl(T a, U b, char)
-> decltype(a * (U(1)/b))
{
return a * (U(1)/b);
}
template <typename T, typename U>
auto smart_division(T&& a, U&& b)
-> decltype(smart_division_impl(std::forward<T>(a), std::forward<U>(b), 0))
{
return smart_division_impl(std::forward<T>(a), std::forward<U>(b), 0);
}
如果你有更多的重载,你可以引入一个辅助类型来确定每个重载的优先级:
template <int I> struct rank : rank<I-1> {};
template <> struct rank<0> {};
template <typename T, typename U>
auto smart_division_impl(T a, U b, rank<2>) -> decltype(a/b)
// ~~~~~~^ highest priority
{
return a/b;
}
template <typename T, typename U>
auto smart_division_impl(T a, U b, rank<1>) -> decltype(a * (U(1)/b))
// ~~~~~~^ mid priority
{
return a * (U(1)/b);
}
template <typename T, typename U>
int smart_division_impl(T a, U b, rank<0>)
// ~~~~~~^ lowest priority
{
return 0;
}
template <typename T, typename U>
auto smart_division(T&& a, U&& b)
-> decltype(smart_division_impl(std::forward<T>(a), std::forward<U>(b), rank<2>{}))
{
return smart_division_impl(std::forward<T>(a), std::forward<U>(b), rank<2>{});
}
同样,rank<2> -> rank<2>
优于 rank<2> -> rank<1>
,后者又优于 rank<2> -> rank<0>
如果两个选项都可以编译,您应该优先选择其中一个选项。例如:
#include <iostream>
template<typename T, typename U>
auto helper(T a, U b, int) -> decltype (a/b) {
std::cout << "first";
return a/b;
}
template<typename T, typename U>
auto helper(T a, U b, ...) -> decltype (a * (U(1)/b)) {
std::cout << "second";
return a * (U(1)/b);
}
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (helper(a, b)) {
return helper(a, b, 0);
}
struct Test {
explicit Test(int) {}
};
int operator / (Test a, Test b) {
return 1;
}
int main() {
std::cout << smart_division(1.0, 2.0);
Test t{5};
std::cout << smart_division(1, t);
return 0;
}
这里如果没有可用的除法,则第二个函数是唯一可用的函数。如果可以除法,则有2个函数:
helper(T, U, int)
和 helper(T, U, ...)
第一个更适合调用 helper(t, u, 1)
请注意,您可能想在 smart_division 中使用完美转发,为了清楚起见,我跳过了它
我想编写一个函数,它在两个不同类型的参数 a
和 b
之间执行除法,如果定义了除法运算符,则使用表达式 a/b
,否则如果没有这样的运算符,返回 a * (1/b)
。
这是我的想法,但我不知道如何在定义了 *
和 /
运算符时禁用第二个定义(或优先考虑第一个定义):
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (a/b) {
return a/b;
}
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (a * (U(1)/b)) {
return a * (U(1)/b);
}
有点难看,但在 gcc 5.2.0 c++14 下对我有用:
template<typename T, typename U, class R = int>
struct smart_division_helper {
auto operator() (T a, U b) -> decltype (a * (U(1)/b)) {
return a*(U(1)/b);
}
};
template<typename T, typename U>
struct smart_division_helper<T, U, decltype(declval<T>()/declval<U>(), 1)> {
auto operator() (T a, U b) -> decltype (a/b) {
return a/b;
}
};
template<class T, class U>
auto smart_division(T a, U b) -> decltype (smart_division_helper<T,U,void>()(a,b)) {
return smart_division_helper<T,U,int>()(a,b);
}
重点是让一个比另一个更专业。所以我们需要偏特化,因此需要 helper class (仿函数)。在此之后,我们有一个使用乘法的通用 class 和一个使用除法的专用 class,但前提是它被允许。
decltype(something, 1)
计算结果为 int
,但前提是 something
正确。
我相信这可以做得更容易。
最简单的技巧是依赖已经定义了优先级规则的重载决策。在下面的解决方案中,附加参数 0
,0 -> int
优于 0 -> char
,因此,如果不被表达式 SFINAE 排除,前者将是首选,而后者仍然可用于回退调用。
#include <utility>
template <typename T, typename U>
auto smart_division_impl(T a, U b, int)
-> decltype(a/b)
{
return a/b;
}
template <typename T, typename U>
auto smart_division_impl(T a, U b, char)
-> decltype(a * (U(1)/b))
{
return a * (U(1)/b);
}
template <typename T, typename U>
auto smart_division(T&& a, U&& b)
-> decltype(smart_division_impl(std::forward<T>(a), std::forward<U>(b), 0))
{
return smart_division_impl(std::forward<T>(a), std::forward<U>(b), 0);
}
如果你有更多的重载,你可以引入一个辅助类型来确定每个重载的优先级:
template <int I> struct rank : rank<I-1> {};
template <> struct rank<0> {};
template <typename T, typename U>
auto smart_division_impl(T a, U b, rank<2>) -> decltype(a/b)
// ~~~~~~^ highest priority
{
return a/b;
}
template <typename T, typename U>
auto smart_division_impl(T a, U b, rank<1>) -> decltype(a * (U(1)/b))
// ~~~~~~^ mid priority
{
return a * (U(1)/b);
}
template <typename T, typename U>
int smart_division_impl(T a, U b, rank<0>)
// ~~~~~~^ lowest priority
{
return 0;
}
template <typename T, typename U>
auto smart_division(T&& a, U&& b)
-> decltype(smart_division_impl(std::forward<T>(a), std::forward<U>(b), rank<2>{}))
{
return smart_division_impl(std::forward<T>(a), std::forward<U>(b), rank<2>{});
}
同样,rank<2> -> rank<2>
优于 rank<2> -> rank<1>
,后者又优于 rank<2> -> rank<0>
如果两个选项都可以编译,您应该优先选择其中一个选项。例如:
#include <iostream>
template<typename T, typename U>
auto helper(T a, U b, int) -> decltype (a/b) {
std::cout << "first";
return a/b;
}
template<typename T, typename U>
auto helper(T a, U b, ...) -> decltype (a * (U(1)/b)) {
std::cout << "second";
return a * (U(1)/b);
}
template<typename T, typename U>
auto smart_division(T a, U b) -> decltype (helper(a, b)) {
return helper(a, b, 0);
}
struct Test {
explicit Test(int) {}
};
int operator / (Test a, Test b) {
return 1;
}
int main() {
std::cout << smart_division(1.0, 2.0);
Test t{5};
std::cout << smart_division(1, t);
return 0;
}
这里如果没有可用的除法,则第二个函数是唯一可用的函数。如果可以除法,则有2个函数:
helper(T, U, int)
和 helper(T, U, ...)
第一个更适合调用 helper(t, u, 1)
请注意,您可能想在 smart_division 中使用完美转发,为了清楚起见,我跳过了它