C ++ 17在两侧构建具有变体的运算符的最佳方法?

C++17 Best way to build operators with variant on two sides?

我有一个项目使用 std::variant 作为可变长度类型:

using Length = std::variant<int, long, float, double, Fraction>;

Fraction class 已经使大多数运算符超载。

我想创建像这样的算术运算符:

Length operator+ (Length lhs, Length rhs);
Length operator- (Length lhs, Length rhs);
Length operator* (Length lhs, Length rhs);
Length operator/ (Length lhs, Length rhs);

但比较运算符也是如此:

Length operator== (Length lhs, Length rhs);
Length operator!= (Length lhs, Length rhs);

Length operator>  (Length lhs, Length rhs);
Length operator>= (Length lhs, Length rhs);
Length operator<  (Length lhs, Length rhs);
Length operator<= (Length lhs, Length rhs);

这是我用 std::visit 方法完成工作的模式。

Length operator* (Length lhs, Length rhs)
{
    Length res;

    std::visit([&res, rhs](auto& left)
    {
        using T = std::remove_cv_t<std::remove_reference_t<decltype(left)>>;

        if constexpr (std::is_same_v<T, int>)
        {
            std::visit([&res, left](auto& right)
            {
                using T = std::remove_cv_t<std::remove_reference_t<decltype(right)>>;

                if constexpr (std::is_same_v<T, int>)       { res = left * right; } else
                if constexpr (std::is_same_v<T, long>)      { res = left * right; } else
                if constexpr (std::is_same_v<T, float>)     { res = left * right; } else
                if constexpr (std::is_same_v<T, double>)    { res = left * right; } else
                if constexpr (std::is_same_v<T, Fraction>)  { res = left * right; }
            },
            rhs);
        } 
        else
        if constexpr (std::is_same_v<T, long>)
        {
            std::visit([&res, left](auto& right)
            {
                using T = std::remove_cv_t<std::remove_reference_t<decltype(right)>>;

                if constexpr (std::is_same_v<T, int>)       { res = left * right; } else
                if constexpr (std::is_same_v<T, long>)      { res = left * right; } else
                if constexpr (std::is_same_v<T, float>)     { res = left * right; } else
                if constexpr (std::is_same_v<T, double>)    { res = left * right; } else
                if constexpr (std::is_same_v<T, Fraction>)  { res = left * right; }
            },
            rhs);
        } 
        else
        if constexpr (std::is_same_v<T, float>)
        {
            std::visit([&res, left](auto& right)
            {
                using T = std::remove_cv_t<std::remove_reference_t<decltype(right)>>;

                if constexpr (std::is_same_v<T, int>)       { res = left * right; } else
                if constexpr (std::is_same_v<T, long>)      { res = left * right; } else
                if constexpr (std::is_same_v<T, float>)     { res = left * right; } else
                if constexpr (std::is_same_v<T, double>)    { res = left * right; } else
                if constexpr (std::is_same_v<T, Fraction>)  { res = left * right; }
            },
            rhs);
        } 
        else
        if constexpr (std::is_same_v<T, double>)
        {
            std::visit([&res, left](auto& right)
            {
                using T = std::remove_cv_t<std::remove_reference_t<decltype(right)>>;

                if constexpr (std::is_same_v<T, int>)       { res = left * right; } else
                if constexpr (std::is_same_v<T, long>)      { res = left * right; } else
                if constexpr (std::is_same_v<T, float>)     { res = left * right; } else
                if constexpr (std::is_same_v<T, double>)    { res = left * right; } else
                if constexpr (std::is_same_v<T, Fraction>)  { res = left * right; }
            },
            rhs);
        } 
        else
        if constexpr (std::is_same_v<T, Fraction>)
        {
            std::visit([&res, left](auto& right)
            {
                using T = std::remove_cv_t<std::remove_reference_t<decltype(right)>>;

                if constexpr (std::is_same_v<T, int>)       { res = left * right; } else
                if constexpr (std::is_same_v<T, long>)      { res = left * right; } else
                if constexpr (std::is_same_v<T, float>)     { res = left * right; } else
                if constexpr (std::is_same_v<T, double>)    { res = left * right; } else
                if constexpr (std::is_same_v<T, Fraction>)  { res = left * right; }
            },
            rhs);
        }
    },
    lhs);

    return res;
}

可行,但这是多余的、不美观的,而且进行大型操作过程可能不会那么快。

由于您的操作数是 variant 类型,因此只需 visit 它们一起:

Length operator* (Length lhs, Length rhs)
{
  return std::visit([](auto const &l, auto const &r) -> Length {
    return l * r;
  }, lhs, rhs);
}

标准库已经为您执行了所需的逻辑。