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

问题描述 投票:0回答:2

我有一个类,它将一个整数包装成一系列只有编译器(和开发人员)知道的值,这些限制在运行时是未知的。该类实现运算符,以便限制发生变化,并返回具有修改限制的新类型的新值(这一点至关重要)。

以下代码给出了该类的示例,实现了所描述的 + 运算符(针对 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,尽管该函数保证在编译时执行。我所需要的只是某种方法来隐式地从公式(在编译时已知)中获取整数文字,以创建使用文字值作为模板参数的所需类型。

c++ templates constexpr implicit-conversion consteval
2个回答
2
投票

:表达式的类型不能依赖于其中文字的值(在任何模板参数或数组范围之外,并且

0
可能是指针的特殊例外)。这又是“
constexpr
函数参数”的问题:也许有一天,但不是现在。


2
投票

如果您不介意输入两个额外的字符,则可以使用数字文字运算符模板

#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
添加一元减运算符的重载。

© www.soinside.com 2019 - 2024. All rights reserved.