6

考虑以下在编译时根据参数类型计算积分或浮点模数的函数:

template<typename T>
constexpr T modulo(const T x, const T y)
{
    return (std::is_floating_point<T>::value) ? (x < T() ? T(-1) : T(1))*((x < T() ? -x : x)-static_cast<long long int>((x/y < T() ? -x/y : x/y))*(y < T() ? -y : y))
    : (static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(x)
      %static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(y));
}

这个函数的主体可以改进吗?(我需要为整数和浮点类型提供一个函数)。

4

6 回答 6

5

这是清理它的一种方法:

#include <type_traits>
#include <cmath>

template <typename T>  //     integral?       floating point?
bool remainder_impl(T a, T b, std::true_type, std::false_type) constexpr
{
    return a % b;  // or whatever
}

template <typename T>  //     integral?        floating point?
bool remainder_impl(T a, T b, std::false_type, std::true_type) constexpr
{
    return std::fmod(a, b); // or substitute your own expression
}

template <typename T>
bool remainder(T a, T b) constexpr
{
    return remainder_impl<T>(a, b,
             std::is_integral<T>(), std::is_floating_point<T>());
}

如果您尝试在非算术类型上调用此函数,则会出现编译器错误。

于 2013-01-12T16:17:21.340 回答
2

我宁愿这样定义它(模板别名+模板重载):

#include <type_traits>

using namespace std;

// For floating point types

template<typename T, typename enable_if<is_floating_point<T>::value>::type* p = nullptr>
constexpr T modulo(const T x, const T y)
{
    return (x < T() ? T(-1) : T(1)) * (
            (x < T() ? -x : x) -
            static_cast<long long int>((x/y < T() ? -x/y : x/y)) * (y < T() ? -y : y)
            );
}

// For non-floating point types

template<typename T>
using TypeToCast = typename conditional<is_floating_point<T>::value, int, T>::type;

template<typename T, typename enable_if<!is_floating_point<T>::value>::type* p = nullptr>
constexpr T modulo(const T x, const T y)
{
    return (static_cast<TypeToCast<T>>(x) % static_cast<TypeToCast<T>>(y));
}

int main()
{
    constexpr int x = modulo(7.0, 3.0);
    static_assert((x == 1.0), "Error!");
    return 0;
}

它更长但更清洁 IMO。我假设“单一功能”是指“可以统一调用的东西”。如果您的意思是“单个函数模板”,那么我只会保留模板别名改进并保留重载。但是,正如另一个答案中提到的,不清楚为什么您确实需要一个单一的功能模板。

于 2013-01-12T15:50:48.087 回答
1

你问,

“这个功能的本体可以改进吗?”

当然。现在它是一团意大利面条:

template<typename T>
constexpr T modulo(const T x, const T y)
{
    return (std::is_floating_point<T>::value) ? (x < T() ? T(-1) : T(1))*((x < T() ? -x : x)-static_cast<long long int>((x/y < T() ? -x/y : x/y))*(y < T() ? -y : y))
    : (static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(x)
      %static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(y));
}

你澄清说……

“(我需要为整数和浮点类型提供一个函数)”

那么模板不是一个单一的功能。这是一个模板。函数是从它生成的。

这意味着您的问题建立在错误的假设之上。

移除该假设后,简化函数体的一种方法(当然应该这样做)是针对浮点类型与其他数字类型专门化模板。为此,将函数模板实现放在一个类中(因为 C++ 不支持函数的部分特化,只支持类)。

然后你可以使用各种格式化技巧,包括 "0?0 : blah" 技巧来使函数更具可读性,包括线条和缩进等等!:-)


附录:深入研究您的代码我看到您随意转换long intint无视调用者的类型。那不好。编写一堆自动化测试用例,使用各种参数类型和大/小值调用函数可能是一个好主意。

于 2013-01-12T15:48:22.897 回答
1
template <class T>
constexpr
T
modulo(T x, T y)
{
    typedef typename std::conditional<std::is_floating_point<T>::value,
                                        int,
                                        T
                                     >::type Int;
    return std::is_floating_point<T>() ?
              x - static_cast<long long>(x / y) * y :
              static_cast<Int>(x) % static_cast<Int>(y);
}
于 2013-01-12T16:49:20.820 回答
0

我相信有更简单的:

// Special available `%`
template <typename T, typename U>
constexpr auto modulo(T const& x, U const& y) -> decltype(x % y) {
    return x % y;
}

注意:基于%so 的检测也适用于自定义类型,只要它们实现了操作符。我在做的时候也做了混合类型。

// Special floating point
inline constexpr float modulo(float x, float y) { return /*something*/; }

inline constexpr double modulo(double x, double y) { return /*something*/; }

inline constexpr long double modulo(long double x, long double y) { return /*something*/; }

fmod注意:不幸的是,我不相信它会更干净constexpr;因此,我选择为浮点类型使用非模板模数,它允许您执行魔术以计算可能基于类型的二进制表示的精确模数。

于 2013-01-12T17:05:28.937 回答
0

如果你愿意,你可以做的更简单:

template<typename A, typename B>
constexpr auto Modulo(const A& a, const B& b) -> decltype(a - (b * int(a/b)))
{
    return a - (b * int(a/b));
}
于 2014-02-11T17:19:26.030 回答