当前位置:  开发笔记 > 编程语言 > 正文

限制C++中的值类型范围

如何解决《限制C++中的值类型范围》经验,为你挑选了3个好方法。

假设我有一个包含值的LimitedValue类,并在int类型'min'和'max'上进行参数化.您可以将它用作容纳值的容器,该值只能在一定范围内.你可以使用它:

LimitedValue< float, 0, 360 > someAngle( 45.0 );
someTrigFunction( someAngle );

这样'someTrigFunction'知道它可以保证提供有效的输入(如果参数无效,构造函数会抛出异常).

但是,复制构造和分配仅限于完全相同的类型.我希望能够做到:

LimitedValue< float, 0, 90 > smallAngle( 45.0 );
LimitedValue< float, 0, 360 > anyAngle( smallAngle );

并在编译时检查该操作,因此下一个示例给出了一个错误:

LimitedValue< float, -90, 0 > negativeAngle( -45.0 );
LimitedValue< float, 0, 360 > postiveAngle( negativeAngle ); // ERROR!

这可能吗?是否有一些实际的方法可以做到这一点,或者有哪些例子可以解决这个问题?



1> 小智..:

您可以使用模板执行此操作 - 尝试以下操作:

template< typename T, int min, int max >class LimitedValue {
   template< int min2, int max2 >LimitedValue( const LimitedValue< T, min2, max2 > &other )
   {
   static_assert( min <= min2, "Parameter minimum must be >= this minimum" );
   static_assert( max >= max2, "Parameter maximum must be <= this maximum" );

   // logic
   }
// rest of code
};


请注意,这是错误的.最后一个示例显示了一个不适合的限制(即-90到0对0到360),但是0的角度满足两个变量类型.因此,如果角度为0而不是-45,则不会得到最后一个示例中标记的错误.

2> Useless..:

好的,这是没有Boost依赖的C++ 11.

在编译时检查类型系统保证的所有内容,并且其他任何内容都会抛出异常.

我添加unsafe_bounded_cast可能抛出的转换,以及safe_bounded_cast静态正确的显式转换(这是多余的,因为复制构造函数处理它,但提供了对称性和表达性).

示例使用

#include "bounded.hpp"

int main()
{
    BoundedValue inner(1);
    BoundedValue outer(2.3);
    BoundedValue overlap(0.0);

    inner = outer; // ok: [0,4] contained in [0,5]

    // overlap = inner;
    // ^ error: static assertion failed: "conversion disallowed from BoundedValue with higher max"

    // overlap = safe_bounded_cast(inner);
    // ^ error: static assertion failed: "conversion disallowed from BoundedValue with higher max"

    overlap = unsafe_bounded_cast(inner);
    // ^ compiles but throws:
    // terminate called after throwing an instance of 'BoundedValueException'
    //   what():  BoundedValueException: !(-1<=2<=1) - BOUNDED_VALUE_ASSERT at bounded.hpp:56
    // Aborted

    inner = 0;
    overlap = unsafe_bounded_cast(inner);
    // ^ ok

    inner = 7;
    // terminate called after throwing an instance of 'BoundedValueException'
    //   what():  BoundedValueException: !(0<=7<=5) - BOUNDED_VALUE_ASSERT at bounded.hpp:75
    // Aborted
}

例外支持

这有点样板,但是如上所述给出了相当可读的异常消息(如果你选择捕获派生的异常类型并且可以对它做一些有用的事情,那么实际的最小/最大/值也会被公开).

#include 
#include 

#define STRINGIZE(x) #x
#define STRINGIFY(x) STRINGIZE( x )

// handling for runtime value errors
#define BOUNDED_VALUE_ASSERT(MIN, MAX, VAL) \
    if ((VAL) < (MIN) || (VAL) > (MAX)) { \
        bounded_value_assert_helper(MIN, MAX, VAL, \
                                    "BOUNDED_VALUE_ASSERT at " \
                                    __FILE__ ":" STRINGIFY(__LINE__)); \
    }

template 
struct BoundedValueException: public std::range_error
{
    virtual ~BoundedValueException() throw() {}
    BoundedValueException() = delete;
    BoundedValueException(BoundedValueException const &other) = default;
    BoundedValueException(BoundedValueException &&source) = default;

    BoundedValueException(int min, int max, T val, std::string const& message)
        : std::range_error(message), minval_(min), maxval_(max), val_(val)
    {
    }

    int const minval_;
    int const maxval_;
    T const val_;
};

template  void bounded_value_assert_helper(int min, int max, T val,
                                                       char const *message = NULL)
{
    std::ostringstream oss;
    oss << "BoundedValueException: !("
        << min << "<="
        << val << "<="
        << max << ")";
    if (message) {
        oss << " - " << message;
    }
    throw BoundedValueException(min, max, val, oss.str());
}

价值等级

template  class BoundedValue
{
public:
    typedef T value_type;
    enum { min_value=Tmin, max_value=Tmax };
    typedef BoundedValue SelfType;

    // runtime checking constructor:
    explicit BoundedValue(T runtime_value) : val_(runtime_value) {
        BOUNDED_VALUE_ASSERT(min_value, max_value, runtime_value);
    }
    // compile-time checked constructors:
    BoundedValue(SelfType const& other) : val_(other) {}
    BoundedValue(SelfType &&other) : val_(other) {}

    template 
    BoundedValue(BoundedValue const &other)
        : val_(other) // will just fail if T, otherT not convertible
    {
        static_assert(otherTmin >= Tmin,
                      "conversion disallowed from BoundedValue with lower min");
        static_assert(otherTmax <= Tmax,
                      "conversion disallowed from BoundedValue with higher max");
    }

    // compile-time checked assignments:
    BoundedValue& operator= (SelfType const& other) { val_ = other.val_; return *this; }

    template 
    BoundedValue& operator= (BoundedValue const &other) {
        static_assert(otherTmin >= Tmin,
                      "conversion disallowed from BoundedValue with lower min");
        static_assert(otherTmax <= Tmax,
                      "conversion disallowed from BoundedValue with higher max");
        val_ = other; // will just fail if T, otherT not convertible
        return *this;
    }
    // run-time checked assignment:
    BoundedValue& operator= (T const& val) {
        BOUNDED_VALUE_ASSERT(min_value, max_value, val);
        val_ = val;
        return *this;
    }

    operator T const& () const { return val_; }
private:
    value_type val_;
};

演员支持

template 
struct BoundedCastHelper
{
    typedef BoundedValue return_type;

    // conversion is checked statically, and always succeeds
    template 
    static return_type convert(BoundedValue const& source)
    {
        return return_type(source);
    }

    // conversion is checked dynamically, and could throw
    template 
    static return_type coerce(BoundedValue const& source)
    {
        return return_type(static_cast(source));
    }
};

template 
auto safe_bounded_cast(BoundedValue const& source)
    -> BoundedValue
{
    return BoundedCastHelper::convert(source);
}

template 
auto unsafe_bounded_cast(BoundedValue const& source)
    -> BoundedValue
{
    return BoundedCastHelper::coerce(source);
}



3> jk...:

Boost Constrained Value库(1)允许您向数据类型添加约束.

但是当你想将它与float类型一起使用时,你必须阅读" 为什么C++的浮点类型不应该与有界对象一起使用? "(如你的例子中所示).

(1) Boost Constrained Value库还不是官方的Boost库.

推荐阅读
路人甲
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有