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

时间:2020-03-06 14:52:20  来源:igfitidea点击:

假设我有一个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!

这可能吗?是否有一些实用的方法可以做到这一点,或者有任何示例可以解决此问题?

解决方案

目前,由于使用C ++规则,即使使用常量参数也要调用方法(以及扩展名,构造函数),这不可能以可移植的方式实现。

在C ++ 0x标准中,我们可以使用const-expr,该常量将允许产生这样的错误。

(这是假设我们仅在实际值非法的情况下才希望它引发错误。如果范围不匹配,则可以实现此目的)

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

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
};

关于模板要记住的一件事是,每次调用一组唯一的模板参数将最终生成一个"唯一"类,对其进行比较和赋值将生成编译错误。可能有些元编程专家可能会解决此问题,但我不是其中之一。我的方法是使用运行时检查以及重载的比较和赋值运算符在一个类中实现这些功能。

我想为Kasprzol解决方案提供一个替代版本:建议的方法始终使用int类型的边界。通过这样的实现,我们可以获得更多的灵活性和类型安全性:

template<typename T, T min, T max>
class Bounded {
private:
    T _value;
public:
    Bounded(T value) : _value(min) {
        if (value <= max && value >= min) {
            _value = value;
       } else {
           // XXX throw your runtime error/exception...
       }
    }
    Bounded(const Bounded<T, min, max>& b)
        : _value(b._value){ }
};

这将使类型检查器可以捕获明显的未命中分配,例如:

Bounded<int, 1, 5> b1(1);
Bounded<int, 1, 4> b2(b1); // <-- won't compile: type mismatch

但是,要检查一个模板实例的范围是否包含在另一个实例的范围内的更高级的关系无法在C ++模板机制中表达。

每个有界规范都会成为一种新类型。因此,编译器可以检查类型不匹配。它无法检查那些类型可能存在的更高级的关系。

Boost约束值库(1)允许我们向数据类型添加约束。

但是我们必须阅读以下建议:"为什么不应该将C ++的浮点类型与有界对象一起使用?"当我们想将其与浮点类型一起使用时(如示例所示)。

(1)Boost约束值库不是官方的Boost库。