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

C++ C+中值类型的限制范围+;,c++,templates,C++,Templates,假设我有一个LimitedValue类,它保存一个值,并在int类型“min”和“max”上参数化。您可以使用它作为容器来保存只能在特定范围内的值。你可以这样使用它: LimitedValue< float, 0, 360 > someAngle( 45.0 ); someTrigFunction( someAngle ); LimitedValuesomeAngle(45.0); someTrigFunction(someAngle); 因此,“someTrigFunction

假设我有一个LimitedValue类,它保存一个值,并在int类型“min”和“max”上参数化。您可以使用它作为容器来保存只能在特定范围内的值。你可以这样使用它:

LimitedValue< float, 0, 360 > someAngle( 45.0 );
someTrigFunction( someAngle );
LimitedValuesomeAngle(45.0);
someTrigFunction(someAngle);
因此,“someTrigFunction”知道它被保证提供有效的输入(如果参数无效,构造函数将抛出异常)

不过,复制构造和赋值仅限于完全相同的类型。我希望能够做到:

LimitedValue< float, 0, 90 > smallAngle( 45.0 );
LimitedValue< float, 0, 360 > anyAngle( smallAngle );
LimitedValue小角度(45.0);
LimitedValue任意角度(小角度);
并在编译时检查该操作,因此下一个示例给出一个错误:

LimitedValue< float, -90, 0 > negativeAngle( -45.0 );
LimitedValue< float, 0, 360 > postiveAngle( negativeAngle ); // ERROR!
LimitedValue负角度(-45.0);
限制值后角(负角);//错误!

这可能吗?是否有一些实际的方法来做这件事,或者在这里有任何例子,例如,

,这是不可能的,因为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
};
templateclass LimitedValue{
模板LimitedValue(const LimitedValue&其他)
{

static_assert(min=max2,“参数最大值必须关于模板,需要记住的一件事是,每次调用一组唯一的模板参数都会生成一个“唯一值”"类的比较和赋值将生成编译错误。可能有一些元编程大师知道如何解决这个问题,但我不是他们中的一员。我的方法是在带有运行时检查和重载比较和赋值运算符的类中实现这些操作。

我想提供另一种方法Kasprzol解决方案的rsion:建议的方法始终使用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){ }
};
模板
类边界{
私人:
T_值;
公众:
有界(T值):\u值(最小值){
如果(值=最小值){
_价值=价值;
}否则{
//XXX抛出运行时错误/异常。。。
}
}
有界(常数有界&b)
:_值(b._值){}
};
这将允许类型检查器捕获明显的未完成分配,例如:

Bounded<int, 1, 5> b1(1);
Bounded<int, 1, 4> b2(b1); // <-- won't compile: type mismatch
有界b1(1);
有界b2(b1);//(1)允许您向数据类型添加约束

但是,当您希望将其与浮点类型一起使用时,必须阅读建议“”(如您的示例所示)


(1) Boost约束值库还不是正式的Boost库。

这实际上是一个复杂的问题,我已经解决了一段时间了

现在我有了一个公开的库,它允许您限制代码中的浮点和整数,这样您就可以更加确保它们在任何时候都是有效的

不仅如此,您还可以在最终版本中关闭限制,这意味着类型与
typedef
几乎相同

将您的类型定义为:

typedef controlled_vars::limited_fauto_init<float, 0, 360> angle_t;
生成这些类是为了让它们包含所有必要的操作符,以便在使用它们时不受太多的影响。这意味着您可以将
角度
视为一个
浮点

angle_t a;
a += 35;
将按预期工作(如果
a+35>360
,则抛出)


我知道这是在2008年发布的……但我没有看到任何指向提供此功能的顶级库的好链接!?

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

类型系统保证的所有内容都在编译时进行检查,其他任何内容都会引发异常

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

示例使用
#包括“bounded.hpp”
int main()
{
内边界值(1);
外部边界值(2.3);
边界值重叠(0.0);
内部=外部;//确定:[0,4]包含在[0,5]中
//重叠=内部;
//^error:静态断言失败:“不允许从最大值较高的BoundedValue转换”
//重叠=安全\u边界\u铸造(内部);
//^error:静态断言失败:“不允许从最大值较高的BoundedValue转换”
重叠=不安全的\u边界\u铸造(内部);
//^编译但抛出:
//在引发“BoundedValueException”实例后调用terminate

//WHO():BooDealEdvExp:Exp::-(1)p>我编写了一个C++类,模仿艾达的<代码>范围< /> > < /P> 它基于模板,类似于此处提供的解决方案

如果在实际项目中使用类似的东西,它将以一种非常基本的方式使用。细微的错误或误解可能是灾难性的

因此,尽管它是一个没有大量代码的小型库,但在我看来,提供单元测试和清晰的设计理念是非常重要的

请随时尝试,如果发现任何问题,请告诉我


bounded::integer库满足您的要求(仅适用于整数类型)

(为了充分披露,我是本图书馆的作者)

它与其他试图以一种重要方式提供“安全整数”的库不同:它跟踪整数边界
angle_t a;
a += 35;
#include "bounded.hpp"

int main()
{
    BoundedValue<int, 0, 5> inner(1);
    BoundedValue<double, 0, 4> outer(2.3);
    BoundedValue<double, -1, +1> 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<double, -1, +1>(inner);
    // ^ error: static assertion failed: "conversion disallowed from BoundedValue with higher max"

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

    inner = 0;
    overlap = unsafe_bounded_cast<double, -1, +1>(inner);
    // ^ ok

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

#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 <typename T>
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 <typename T> 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<T>(min, max, val, oss.str());
}
template <typename T, int Tmin, int Tmax> class BoundedValue
{
public:
    typedef T value_type;
    enum { min_value=Tmin, max_value=Tmax };
    typedef BoundedValue<value_type, min_value, max_value> 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 <typename otherT, int otherTmin, int otherTmax>
    BoundedValue(BoundedValue<otherT, otherTmin, otherTmax> 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 <typename otherT, int otherTmin, int otherTmax>
    BoundedValue& operator= (BoundedValue<otherT, otherTmin, otherTmax> 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 <typename dstT, int dstMin, int dstMax>
struct BoundedCastHelper
{
    typedef BoundedValue<dstT, dstMin, dstMax> return_type;

    // conversion is checked statically, and always succeeds
    template <typename srcT, int srcMin, int srcMax>
    static return_type convert(BoundedValue<srcT, srcMin, srcMax> const& source)
    {
        return return_type(source);
    }

    // conversion is checked dynamically, and could throw
    template <typename srcT, int srcMin, int srcMax>
    static return_type coerce(BoundedValue<srcT, srcMin, srcMax> const& source)
    {
        return return_type(static_cast<srcT>(source));
    }
};

template <typename dstT, int dstMin, int dstMax,
          typename srcT, int srcMin, int srcMax>
auto safe_bounded_cast(BoundedValue<srcT, srcMin, srcMax> const& source)
    -> BoundedValue<dstT, dstMin, dstMax>
{
    return BoundedCastHelper<dstT, dstMin, dstMax>::convert(source);
}

template <typename dstT, int dstMin, int dstMax,
          typename srcT, int srcMin, int srcMax>
auto unsafe_bounded_cast(BoundedValue<srcT, srcMin, srcMax> const& source)
    -> BoundedValue<dstT, dstMin, dstMax>
{
    return BoundedCastHelper<dstT, dstMin, dstMax>::coerce(source);
}
auto x = bounded::checked_integer<0, 7>(f());
auto y = 7_bi;
auto z = x + y;
// decltype(z) == bounded::checked_integer<7, 14>
static_assert(z >= 7_bi);
static_assert(z <= 14_bi);
z = 10_bi;
z = x;
static_assert(!std::is_assignable<decltype((z)), decltype(0_bi)>::value);