如何在编译时检查值和限制

时间:2018-02-16 12:31:47

标签: c++ constexpr c++17

我想创建一个设置类,其中ID列表具有默认值和值的限制。所有使用constexpr都允许进行编译时检查。

在编译时,我还想验证默认值与限制,以确保没有设置非法值。在这里,我撞墙了。

所以,我的基本信息如下:

using item = std::variant<bool, int64_t, double, std::string_view>;

enum class ID {
    fs,
    fc,
    fosc
};


struct ItemLimit
{
    constexpr ItemLimit( item low, item high ) :
        Low( low ),
        High( high ){}

    const item Low;
    const item High;
};

struct item_entry{
    ID id;
    item default_value;
    ItemLimit limit;
};

我希望能够通过以下方式编写列表:

constexpr item_entry item_list[] = {
    {ID::fs,    12.0,               Limit( -12.0, 32.0 )},
    {ID::fc,    1244,               Limit( 4, 12333 )},
    {ID::fc,    false},
    {ID::fc,    5'000'000'000,      Limit( 1, 9999999999999999 )},
    {ID::fosc,  "HELLOOOOO"}
};

这需要一组构造函数,我将在下面的讨论中将其限制为整数项。

Limit和item_entry现在看起来像这样:

template <typename T>
struct ValueLimit
{
    constexpr ValueLimit( T low, T high ) :
        low( low ),
        high( high )
    {
    };

    const T low;
    const T high;
};

constexpr ValueLimit<int64_t> Limit( long long x, long long y ){
    return ValueLimit<int64_t>( x, y );
}


struct item_entry{
    constexpr item_entry( ID id, long long value, ValueLimit<int64_t> limit ) :
        id( id ),
        default_value( int64_t( value ) ),
        limit( limit.low, limit.high )
    {}


    ID id;
    item default_value;
};

在item_entry构造函数中,我想检查值是否在限制范围内,但我无法弄清楚如何。我的所有努力都以“不被评估为常数”的表达式结束。

理想情况下,解决方案也适用于浮点值。

提前致谢!

Henrik Andresen

1 个答案:

答案 0 :(得分:2)

问题是value在此上下文中不是常量表达式,因为函数参数永远不是常量表达式

constexpr item_entry( ID id, long long value, ValueLimit<int64_t> limit ) 
                             ^~~~~~~~~~~~~~~

您需要传递value,使其可以作为常量表达式的一部分使用:std::integral_constant正是您所需要的。

template <long long X> // <==
constexpr item_entry( ID id, 
                      std::integral_constant<long long,X> value, // <==
                      ValueLimit<int64_t> limit ) 
{
    static_assert(value >= limit.low && value <= limit.high); // <==
}

同样的原则适用于limit

template <typename T, T Low, T High>
struct ValueLimit
{
    static constexpr T low = Low;
    static constexpr T high = High;
};

最后的修改:

struct item_entry
{
    template <long long X, typename Limit>
    constexpr item_entry( ID id, std::integral_constant<long long, X> value, Limit ) :
        id( id ),
        default_value( int64_t( value ) )
    {
        static_assert(value >= Limit::low && value <= Limit::high);        
    }

    ID id;
    item default_value;
};

用法示例:

template <long long X>
constexpr std::integral_constant<long long, X> ic{};

template <int64_t Low, int64_t High>
constexpr ValueLimit<int64_t, Low, High> limit{};

constexpr item_entry item_list[] = {
    {ID::fc,    ic<1244>,               limit< 4, 12333 >},
    {ID::fc,    ic<5'000'000'000>,      limit< 1, 9999999999999999 >}
};

live example on wandbox.org