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

pqn*_*net 17 c++ templates sfinae c++11

我想编写一个函数,它在两个参数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);
}
Run Code Online (Sandbox Code Playgroud)

Pio*_*cki 20

最简单的技巧是依赖已经定义其优先级规则的重载决策.在下面的溶液中,用一个额外的参数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);
}
Run Code Online (Sandbox Code Playgroud)

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>{});
}
Run Code Online (Sandbox Code Playgroud)

演示2

在这里再次rank<2> -> rank<2>优于rank<2> -> rank<1>反过来rank<2> -> rank<0>


Ria*_*iaD 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;
}
Run Code Online (Sandbox Code Playgroud)

这里如果没有可用的除法,则第二个函数是唯一可用的函数.如果划分可用,则有2个功能:

helper(T, U, int)并且helper(T, U, ...)第一个更适合呼叫helper(t, u, 1)

DEMO

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