C++ 如果未实施除法运算符,SFINAE将采取回退措施

C++ 如果未实施除法运算符,SFINAE将采取回退措施,c++,templates,c++11,sfinae,C++,Templates,C++11,Sfinae,我想写一个函数,在两个不同类型的参数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<typenam

我想写一个函数,在两个不同类型的参数
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);
}
模板
自动智能分割(T a,U b)->数据类型(a/b){
返回a/b;
}
模板
自动智能分割(T a,U b)->数据类型(a*(U(1)/b)){
返回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);
}
模板
结构智能分区辅助程序{
自动运算符()(ta,ub)->decltype(a*(U(1)/b)){
返回a*(U(1)/b);
}
};
模板
结构智能分区辅助程序{
自动运算符()(T a,U b)->取消类型(a/b){
返回a/b;
}
};
模板
自动智能分割(ta,ub)->decltype(智能分割辅助对象()(a,b)){
返回智能分区帮助器()(a,b);
}
重点是使一个比另一个更专业。所以我们需要部分专门化,因此需要助手类(函子)。在此之后,我们有一个使用乘法的通用类和一个使用除法的专用类,但前提是它是允许的

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);
}
#包括
模板
自动智能分区实现(TA、UB、int)
->数据类型(a/b)
{
返回a/b;
}
模板
自动智能分区实现(TA、UB、char)
->decltype(a*(U(1)/b))
{
返回a*(U(1)/b);
}
模板
汽车智能化部门(T&a、U&b)
->decltype(智能分区实现(std::forward(a),std::forward(b),0))
{
返回智能分区impl(std::forward(a),std::forward(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>{});
}
模板结构秩:秩{};
模板结构秩{};
模板
自动智能分区实现(TA、UB、rank)->数据类型(a/b)
//~~~~~~~^最高优先级
{
返回a/b;
}
模板
自动智能分区实现(TA、UB、rank)->decltype(a*(U(1)/b))
//~~~~~~~^中等优先级
{
返回a*(U(1)/b);
}
模板
内部智能分区实施(TA、UB、等级)
//~~~~~~~^最低优先级
{
返回0;
}
模板
汽车智能化部门(T&a、U&b)
->decltype(智能分区实现(std::forward(a),std::forward(b),秩{}))
{
返回smart_division_impl(std::forward(a),std::forward(b),秩{});
}


这里再次说明,
rank->rank
rank->rank
更好,后者比
rank->rank
更好。如果两者都可以编译,您应该选择其中一个选项。例如:

#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;
}
#包括
模板
自动辅助程序(TA、UB、int)->decltype(a/b){
标准::cout decltype(a*(U(1)/b)){
std::cout decltype(助手(a,b)){
返回助手(a、b、0);
}
结构测试{
显式测试(int){}
};
int运算符/(测试a、测试b){
返回1;
}
int main(){

好吧,你试过什么了?我想你应该加上一些解释,因为OP说“但我不知道如何禁用第二个定义(或优先考虑第一个定义)”我感觉我眨了眨眼,元编程世界从标记分派转移到跟踪返回SFINAE失败+首选隐式转换,这是什么时候发生的?if
operator/(T,U)
定义,第一个将是调用,因为
0没有数据转换
现在我读了解决方案,我想我应该知道这一点,因为我在过去的某个地方读过类似的东西,我觉得很傻。我选择这个答案是因为它还解释了将方法扩展到多个优先级的一般方法。在旁注,在标准库中使用类似于
rank
模板的东西不是很有用吗?这样就有了解决此类问题的标准解决方案吗?在
smart\u division\u impl
函数中不需要完美转发吗?出于同样的原因,我也跳过了问题中的完美转发。