c++ 是否可以从公式中的文字整数中以某种方式导出模板参数?

tnkciper  于 2023-08-09  发布在  其他
关注(0)|答案(2)|浏览(89)

我有一个类,它将一个整数 Package 成一个只有编译器(和开发人员)知道的值范围,在运行时限制是未知的。类实现了操作符,这样限制就改变了,并返回一个新类型的新值,该值具有修改后的限制(这是至关重要的)。
下面的代码给出了类的一个例子,实现了+运算符(在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;


当然,我可以显式地 Package 字面整数以获得所需的结果:

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,尽管该函数保证在编译时执行。我所需要的只是从公式(编译时已知)中隐式地获取整数字面量,以创建使用字面量值作为模板参数的所需类型。

jfgube3f

jfgube3f1#

:表达式的类型不能依赖于其中的文字值(在任何模板参数或数组边界之外,0可能是指针的特殊例外)。这是“constexpr函数参数”问题的重演:也许有一天但不是现在

q43xntqr

q43xntqr2#

如果不介意多输入两个字符,可以使用数字文字运算符模板

#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添加一元减号运算符的重载。
Edit:通过使用递归,不需要将""_r作为模板就可以做到这一点。

相关问题