如果未实施除法运算符,则SFINAE回退

时间:2015-12-22 15:21:30

标签: c++ templates c++11 sfinae

我想编写一个函数,在不同类型的两个参数ab之间执行除法,如果定义了除法运算符,则使用表达式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);
}

3 个答案:

答案 0 :(得分:18)

最简单的技巧是依赖已经定义其优先级规则的重载决策。在下面的解决方案中,使用附加参数00 -> 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);
}

DEMO

如果你有更多的重载,你可以改为引入一个帮助器类型来优先考虑每个:

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>{});
}

DEMO 2

同样,rank<2> -> rank<2>优于rank<2> -> rank<1>,而rank<2> -> rank<0>优先于datagridview_MouseMove

答案 1 :(得分:5)

如果两者都可以编译,你应该选择其中一个选项。例如:

#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)

DEMO

请注意,您可能希望在smart_division中使用完美转发,为了清晰起见,我跳过它

答案 2 :(得分:2)

有点难看,但在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);
}

关键是要使一个人比另一个人更专业。所以我们需要部分特化,因此需要辅助类(仿函数)。在此之后,我们有一个使用乘法的通用类,以及一个使用除法的专用类,但前提是它是允许的。

decltype(something, 1)评估为int,但前提是something是正确的。

我相信这可以更轻松。