Проверка во время компиляции, находится ли указанное значение в диапазоне типа - PullRequest
2 голосов
/ 05 апреля 2011

Можно ли это проверить:

template<class IntType,IntType value>
struct X{};

Под этим я подразумеваю, можно ли проверить, что значение, предоставленное пользователем, будет "вписываться" в IntType (который может быть любым из целых чисел std)типы) тип?Например, я хотел бы обнаружить что-то вроде этого:

X<char,300> a;//here 300 is out of range and I would like to be able to detect that.

Ответы [ 4 ]

2 голосов
/ 05 апреля 2011

Теперь, когда вы изменили подпись X по сравнению с оригинальным неотредактированным вопросом, ее легко реализовать, используя Boost . Integer :

#include <boost/static_assert.hpp>
#include <boost/cstdint.hpp>
#include <boost/integer_traits.hpp>

template<
    typename IntType,
    boost::uint64_t Value,
    bool IsSigned = boost::integer_traits<IntType>::is_signed
>
struct validate_range;

template<typename IntType, boost::uint64_t Value>
struct validate_range<IntType, Value, true>
{
    typedef boost::integer_traits<IntType> traits_t;
    static bool const value =
        static_cast<boost::int64_t>(Value) >= traits_t::const_min &&
        static_cast<boost::int64_t>(Value) <= traits_t::const_max;
};

template<typename IntType, boost::uint64_t Value>
struct validate_range<IntType, Value, false>
{
    typedef boost::integer_traits<IntType> traits_t;
    static bool const value =
        Value >= traits_t::const_min &&
        Value <= traits_t::const_max;
};

template<typename IntType, boost::uint64_t Value>
struct X
{
    BOOST_STATIC_ASSERT_MSG(
        (validate_range<IntType, Value>::value),
        "Value constant is out of range"
    );
};

int main()
{
    X<char, -2> x1;             // fails iif char is unsigned by default
    X<char, 2> x2;              // fine
    X<char, 255> x3;            // fails iif char is signed by default
    X<unsigned char, -2> x4;    // fails
    X<unsigned char, 255> x5;   // fine
    X<unsigned char, 300> x6;   // fails
}
2 голосов
/ 05 апреля 2011

Boost - правильный путь, но вы хотите, чтобы вы действительно хотели получить то, что следует новому стандарту C ++ 0x: статические утверждения.Boost уже реализует это в boost_staticassert .

1 голос
/ 05 апреля 2011

Нет. Учитывая ваш код, 300 преобразуется компилятором в char до того, как вы его увидите.

Самое близкое, что вы можете сделать, это принять аргумент в целочисленный параметр, чей диапазон больше, чем ваш целевой тип. Затем проверьте, что значение будет соответствовать перед преобразованием. Единственная проблема - signed против unsigned, для которой я не думаю, что есть общее решение.

Но не беспокойтесь: задача вашего класса не в том, чтобы убедиться, что аргументы предоставлены правильно; это будет работа типа утилиты, который просто не существует. К лучшему или к худшему, C ++ не предоставляет чистого механизма для этого, поскольку предполагает, что программист не допустит этих ошибок.

0 голосов
/ 04 марта 2019

Я бы сказал, что прямым решением этого вопроса может быть следующее:

   template< typename T, T X, T L, T H>
      using inside_t = 
        std::enable_if_t< (X <= H) && (X >= L), 
           std::integral_constant<T, X> >;

Применительно к ОП:

    template<typename C, unsigned K>    struct X; // final {};

template<unsigned K>
struct X<char, K> final 
{
    using ascii_ordinal = inside_t<unsigned, K, 0, 127>;
    char value = char(ascii_ordinal::value);
};

Который выдает действительно ужасные сообщения об ошибках CL, когда выполняет свою работу:

X<char, 300> a; //here 300 is out of range and I would like to be able to detect that.

Хотя гораздо менее привлекательный, но наиболее удобный API может быть:

template<unsigned K>
struct X<char, K> final 
{
    static_assert( K >= 0U && K <= 127U, "\n\nTeribly sorry, but value must be between 0 and 127 inclusive\n\n") ;
    char value = char(K);
};
...