我有一个类,它将一个整数包装成一系列只有编译器(和开发人员)知道的值,这些限制在运行时是未知的。该类实现运算符,以便限制发生变化,并返回具有修改限制的新类型的新值(这一点至关重要)。
以下代码给出了该类的示例,实现了所描述的 + 运算符(针对 c++20 进行编译):
#include <iostream>
using namespace std;
template< int LOWER_, int UPPER_ >
class MyRange final {
public:
constexpr static int LOWER = LOWER_;
constexpr static int UPPER = UPPER_;
template< int, int >
friend class MyRange;
constexpr MyRange(MyRange const &) noexcept = default;
constexpr MyRange(MyRange&&) noexcept = default;
constexpr ~MyRange() {}
template< int VALUE >
requires ( VALUE >= LOWER && VALUE <= UPPER )
static constexpr
MyRange wrap = MyRange( VALUE );
template< class _RHS, int _RHS_LOWER = _RHS::LOWER, int _RHS_UPPER = _RHS::UPPER,
int _RES_LOWER = LOWER + _RHS_LOWER, int _RES_UPPER = UPPER + _RHS_UPPER,
typename _RESULT_T = MyRange<_RES_LOWER, _RES_UPPER> >
friend
_RESULT_T
operator+(MyRange const lhs, _RHS const &rhs) noexcept {
int result = lhs.value + rhs.unwrap();
return construct<_RESULT_T>( result );
}
int unwrap() const noexcept { return value; }
private:
MyRange() = delete;
MyRange& operator=(MyRange const &) = delete;
MyRange& operator=(MyRange&&) = delete;
// this must not be exposed because value has to be checked against limits at compile-time;
// wrap<VALUE> is the public "constructor"
explicit constexpr MyRange(int value) noexcept : value(value) {}
// helper: construct another specialization of MyRange
template< class TO >
static constexpr TO construct(int value) noexcept { return TO(value); }
int const value;
};
如何使用:
int main() {
auto value = MyRange<5,20>::wrap<8>;
auto another = MyRange<6,10>::wrap<6>;
auto result = value + another;
// 14; limits: 11, 30
cout << result.unwrap() << "; limits: " << decltype(result)::LOWER << ", " << decltype(result)::UPPER << endl;
}
现在我遇到以下问题。我希望能够将整数文字添加到范围类类型的变量中。这可以通过显式或隐式转换来实现,但这会使限制不必要地爆炸:
using Range = MyRange<5,20>;
auto value = Range::wrap<8>;
auto result = value + Range::wrap<6>;
// 14; limits: 10, 40
cout << result.unwrap() << "; limits: " << decltype(result)::LOWER << ", " << decltype(result)::UPPER << endl;
当然,我可以显式包装文字整数以获得所需的结果:
auto value = MyRange<5,20>::wrap<8>;
auto result = value + MyRange<6,6>::wrap<6>;
// 14; limits: 11, 26
cout << result.unwrap() << "; limits: " << decltype(result)::LOWER << ", " << decltype(result)::UPPER << endl;
但我不喜欢它。用户端的开销太大。我宁愿写类似
auto result = value + 6;
的内容,并且整数文字 6
在传递给运算符之前会隐式转换为 MyRange<6,6>::wrap<6>
。我已经尝试使用带有值参数的 consteval 函数,该函数用于创建所需的 MyRange 类型,但不幸的是 consteval 函数的参数不是 constexpr,尽管该函数保证在编译时执行。我所需要的只是某种方法来隐式地从公式(在编译时已知)中获取整数文字,以创建使用文字值作为模板参数的所需类型。
否:表达式的类型不能依赖于其中文字的值(在任何模板参数或数组范围之外,并且
0
可能是指针的特殊例外)。这又是“constexpr
函数参数”的问题:也许有一天,但不是现在。
如果您不介意输入两个额外的字符,则可以使用数字文字运算符模板:
#include <iostream>
#include <cctype>
#include <algorithm>
template< int LOWER_, int UPPER_ >
class MyRange final {
public:
constexpr static int LOWER = LOWER_;
constexpr static int UPPER = UPPER_;
template< int, int >
friend class MyRange;
constexpr MyRange(MyRange const &) noexcept = default;
constexpr MyRange(MyRange&&) noexcept = default;
constexpr ~MyRange() {}
template< int VALUE >
requires ( VALUE >= LOWER && VALUE <= UPPER )
static constexpr
MyRange wrap = MyRange( VALUE );
template< class _RHS, int _RHS_LOWER = _RHS::LOWER, int _RHS_UPPER = _RHS::UPPER,
int _RES_LOWER = LOWER + _RHS_LOWER, int _RES_UPPER = UPPER + _RHS_UPPER,
typename _RESULT_T = MyRange<_RES_LOWER, _RES_UPPER> >
friend
_RESULT_T
constexpr operator+(MyRange const lhs, _RHS const &rhs) noexcept {
int result = lhs.value + rhs.unwrap();
return construct<_RESULT_T>( result );
}
int unwrap() const noexcept { return value; }
private:
MyRange() = delete;
MyRange& operator=(MyRange const &) = delete;
MyRange& operator=(MyRange&&) = delete;
// this must not be exposed because value has to be checked against limits at compile-time;
// wrap<VALUE> is the public "constructor"
explicit constexpr MyRange(int value) noexcept : value(value) {}
// helper: construct another specialization of MyRange
template< class TO >
static constexpr TO construct(int value) noexcept { return TO(value); }
int const value;
};
constexpr int charDigitToInt(char c, std::size_t digit)
{
int result = c - '0';
for (size_t i = 0; i < digit; i++)
{
result *= 10;
}
return result;
}
template <std::size_t size, std::size_t digit>
constexpr int charArrayToInt(const char chars[size])
{
if constexpr (digit == (std::size_t)-1)
{
return 0;
}
else
{
return charDigitToInt(chars[size - digit - 1], digit) + charArrayToInt<size, digit - 1>(chars);
}
}
template <char ...Chars>
constexpr auto operator ""_r()
{
constexpr std::size_t length = sizeof...(Chars);
constexpr char chars[length]{ Chars... };
static_assert(std::all_of(chars, chars + length, [](char c) { return isdigit(c); }), "The argument to _r must be a positive integer");
constexpr int value = charArrayToInt<length, length - 1>(chars);
return MyRange<value, value>::template wrap<value>;
}
int main()
{
auto value = MyRange<5,20>::wrap<8>;
auto result = value + 6_r;
// 14; limits: 11, 26
std::cout << result.unwrap() << "; limits: " << decltype(result)::LOWER << ", " << decltype(result)::UPPER << std::endl;
}
这里,
digit
表示数字的指数,因此对于个位,digit
为0,对于十位,digit
为1,依此类推。
但是,数字文字运算符模板不适用于负数,因此要支持它,您需要向
MyRange
添加一元减运算符的重载。