在C ++模板中键入条件?

时间:2010-06-23 18:34:14

标签: c++ templates

我在C#中有一个方法如下(在一个范围内包含一个数字,比如说0到360 ...如果你传递0-359就得到相同的值,如果你传递360就得到0,361得到1等等):

    /// <summary>
    /// Wraps the value across the specified boundary range.
    /// 
    /// If the value is in the range <paramref name="min"/> (inclusive) to <paramref name="max"/> (exclusive),
    /// <paramref name="value"/> will be returned. If <paramref name="value"/> is equal to <paramref name="max"/>,
    /// <paramref name="min"/> will be returned. The method essentially creates a loop between <paramref name="min"/>
    /// and <paramref name="max"/>.
    /// </summary>
    /// <param name="value">The value to wrap.</param>
    /// <param name="min">The minimum value of the boundary range, inclusive.</param>
    /// <param name="max">The maximum value of the boundary range, exclusive.</param>
    /// <returns>The value wrapped across the specified range.</returns>
    public static T Wrap<T>(T value, T min, T max) where T : IComparable<T>
    {
        // If it's positive or negative infinity, we just return the minimum, which is the "origin"
        bool infinityDouble = typeof(T) == typeof(double) && (double.IsPositiveInfinity(Convert.ToDouble(value)) || double.IsNegativeInfinity(Convert.ToDouble(value)));
        bool infinityFloat = typeof(T) == typeof(float) && (float.IsPositiveInfinity(Convert.ToSingle(value)) || float.IsNegativeInfinity(Convert.ToSingle(value)));
        if (infinityDouble || infinityFloat)
        {
            return min;
        }

        // If the value is between the origin (inclusive) and the maximum value (exclusive), just return the value
        if (value.CompareTo(min) >= 0 && value.CompareTo(max) < 0)
        {
            return value;
        }

        // The range of the wrapping function
        var range = (dynamic)max - (dynamic)min;

        return ((((value % range) + range) - min) % range) + min;
    }

我在C ++中也需要这个方法,我定义如下:

/*!
    Wraps the value across the specified boundary range.

    If the value is in the range \a min (inclusive) to \a max (exclusive), \a value will be returned.
    If \a value is equal to \a max, \a min will be returned. The method essentially creates a loop between
    \a min and \a max.

    \param value The value to wrap.
    \param min The minimum value of the boundary range, inclusive.
    \param max The maximum value of the boundary range, exclusive.
    \return The value wrapped across the specified range.
 */
template <typename T> const T& MathHelper::wrap(const T &value, const T &min, const T &max)
{
    // If it's positive or negative infinity, we just return the minimum, which is the "origin"
    bool infinityDouble = value == std::numeric_limits<double>::infinity() || value == -std::numeric_limits<double>::infinity();
    bool infinityFloat = value == std::numeric_limits<float>::infinity() || value == -std::numeric_limits<float>::infinity();
    if (infinityDouble || infinityFloat)
    {
        return min;
    }

    // If the value is between the origin (inclusive) and the maximum value (exclusive), just return the value
    if (value >= min && value < max)
    {
        return value;
    }

    // The range of the wrapping function
    T range = max - min;

    return ((((value % range) + range) - min) % range) + min;
}

现在我的问题是:我是否在C ++版本中正确检查无限?我看不出任何方式说“如果加倍,做这些检查,如果浮动,做这些检查”。如果它不是我想要的类型,它会返回false吗? 另外,为什么没有为float和double定义%运算符?我想我必须自己实现模运算符。该方法几乎适用于数字类型 - byte,short,int,long,float,double。

3 个答案:

答案 0 :(得分:3)

使用numeric_limits提供的工具,您无需使用任何复杂的专业化或类似的东西进行无穷大检查。

template <typename T> 
const T& MathHelper::wrap(const T &value, const T &min, const T &max) {
  bool isInfinity = std::numeric_limits<T>::has_infinity() 
                 && (std::abs(value) == std::numeric_limits<T>::infinity());
  //the rest
}

涉及operator%的最后一步会更复杂。您需要提供一个自定义的mod函数,该函数会重载以将浮点类型传递到std::modf而不是operator%。你或许可以使用类型特征[通过boost或TR1]来最小化这个的重复方面,虽然我不确定这样做最优雅的方法是什么。也许有些事情如下:

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type mod(T, T) {
   //use std::modf
}

template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type mod(T, T) {
   //use %
}

答案 1 :(得分:2)

科林评论的一个具体例子:

#include <iostream>
#include <limits>

template<typename T>
class Foo {
    public:
    const T& wrap (const T& v, const T& min, const T& max) {
        // ...
        return v;
    }

};

template<>
class Foo<double> {
    public:
    const double& wrap (const double& v, const double& miun, const double& max) {
        if (v == std::numeric_limits<double>::infinity ()) {
            std::cout << "It was infinity" << std::endl;
        }
        return v;
    }
};


int main () {
    Foo<double> fd;
    Foo<long> fl;

    std::cout << "fl.wrap(42, 0, 100)      : " << fl.wrap(42, 0, 100) << std::endl;
    std::cout << "fd.wrap(inf, 0.0, 100.0) : " <<
        fd.wrap (std::numeric_limits<double>::infinity (), 0.0, 100.0) << std::endl;
    return 0;

}

哪个收益率:

fl.wrap(42, 0, 100)      : 42
It was infinity
fd.wrap(inf, 0.0, 100.0) : inf

答案 2 :(得分:0)

你有fmod for float modulus(以cmath为单位)。注意符号约定(结果与第一个操作数的符号相同)。

检查无穷大是好的。但是你忘了检查NaN。