创建一个强制始终在指定范围内的自定义int; 如何通过整数溢出?

gaa*_*kam 6 c++ integer-overflow

如标题中所示.作为练习,我想创建一个int来强制对其值进行约束,并且不允许将其设置为超出其指定范围的值.

以下是我尝试解决此问题的方法:

#include <cassert>
#include <cstdint>
#include <iostream>
using namespace std;

int main();

template<typename valtype, valtype minval, valtype maxval>
class ConstrainedValue
{
  valtype val;

  static bool checkval (valtype val)
  {
    return minval <= val && val <= maxval;
  }

public:
  ConstrainedValue() : val{minval} // so that we're able to write ConstrainedValue i;
  {
    assert(checkval(val));
  }

  ConstrainedValue(valtype val) : val{val}
  {
    assert(checkval(val));
  }

  ConstrainedValue &operator = (valtype val)
  {
    assert(checkval(val));
    this->val = val;
    return *this;
  }

  operator const valtype&() // Not needed here but can be; safe since it returns a const reference
  {
    return val;
  }

  friend ostream &operator << (ostream& out, const ConstrainedValue& v) // Needed because otherwise if valtype is char the output could be bad
  {
    out << +v.val;
    return out;
  }

  friend istream &operator >> (istream& in, ConstrainedValue& v) // this is horrible ugly; I'd love to know how to do it better
  {
    valtype hlp;
    auto hlp2 = +hlp;
    in >> hlp2;
    assert(checkval(hlp2));
    v.val = hlp2;
    return in;
  }
};

int main()
{
  typedef ConstrainedValue<uint_least8_t, 0, 100> ConstrainedInt;
  ConstrainedInt i;
  cin >> i;
  cout << i;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

问题是......这不起作用.如果给定此自定义整数的值超出其基础类型,则只会设置错误的值.

例如,假设我们的范围约束为[0; 100],底层类型是uint_least8_t,如上例所示.uint_least8_t计算为char或unsigned char,我不确定是哪一个.让我们尝试用不同的值来提供这个程序:

10
10
Run Code Online (Sandbox Code Playgroud)

尼斯.作品.

101
test: test.cpp:52: std::istream& operator>>(std::istream&, ConstrainedValue<unsigned int, 0u, 100u>&): Assertion `checkval(hlp2)' failed.
Aborted
Run Code Online (Sandbox Code Playgroud)

哈哈!正是我想要的.

但:

257
1
Run Code Online (Sandbox Code Playgroud)

是啊.溢出,截断,错误值,无法正确检查范围.

如何解决这个问题?

Tar*_*ama 0

我认为不可能在进入构造函数的过程中检测到溢出,因为它是为了满足构造函数参数而执行的,所以一旦它到达构造函数主体,它就已经溢出了。

一种可能的解决方法是在界面中接受大类型,然后进行检查。例如,您可以long int在界面中采用 s,然后将它们在内部存储为valtype. 由于无论如何您都会执行边界检查,因此它应该相当安全。