2014-01-10 2 views
2

Я думаю, что я столкнулся с ошибкой с шаблоном C++ 11 std :: basic_type.Это ошибка с std :: basic_type

Я использую класс признаков для определения диапазонов перечислений, которые мы имеем в нашей системе. Тогда я могу предоставить общую функцию is_valid.

Я недавно расширил функцию, когда -Wextra был включен, потому что я получал много предупреждений о всегда истинном сравнении.

Когда перечисление имеет неподписанный тип, а его первое значение равно 0, предупреждение было сгенерировано.

Решил, что легко. Но на следующий день некоторые модульные тесты в модулях с использованием функции начались с .

Если вы не укажете базовый тип перечисления, он по-прежнему выбирает правильную реализацию, но как-то возвращает неправильный результат.

Вот минимальный пример (http://ideone.com/PwFz15):

#include <type_traits> 
#include <iostream> 

using namespace std; 

enum Colour 
{ 
    RED = 0, 
    GREEN, 
    BLUE 
}; 

enum NoProblems : int 
{ 
    A, 
    B, 
    C 
}; 

enum AlsoOk : unsigned 
{ 
    D, 
    E, 
    F 
}; 


template <typename Enum> struct enum_traits; 

template <> struct enum_traits<Colour> 
{ 
    typedef Colour type; 
    static constexpr type FIRST = RED; 
    static constexpr type LAST = BLUE; 
}; 

template <> struct enum_traits<NoProblems> 
{ 
    typedef NoProblems type; 
    static constexpr type FIRST = A; 
    static constexpr type LAST = C; 
}; 

template <> struct enum_traits<AlsoOk> 
{ 
    typedef AlsoOk type; 
    static constexpr type FIRST = D; 
    static constexpr type LAST = F; 
}; 


#if 0 
// This implementation gives you warnings about an always true comparison 
// ONLY IF you define the underlying type of your enum, such as Colour. 
template <typename Enum> 
inline constexpr bool is_valid(Enum e) 
{ 
    return e >= enum_traits<Enum>::FIRST && e <= enum_traits<Enum>::LAST; 
} 
#endif 

// So you define the is_valid function like so, to prevent the warnings: 

template <typename Enum, typename enable_if<is_unsigned<typename underlying_type<Enum>::type>::value && enum_traits<Enum>::FIRST == 0, int>::type = 0> 
inline constexpr bool is_valid(Enum e) 
{ 
    return e <= enum_traits<Enum>::LAST; 
} 

template <typename Enum, typename enable_if<is_signed<typename underlying_type<Enum>::type>::value || enum_traits<Enum>::FIRST != 0, int>::type = 0> 
inline constexpr bool is_valid(Enum e) 
{ 
    return e >= enum_traits<Enum>::FIRST && e <= enum_traits<Enum>::LAST; 
} 



int main() 
{ 
    Colour c = static_cast<Colour>(RED - 1); 
    cout << is_valid(c) << endl; 

    NoProblems np = static_cast<NoProblems>(A - 1); 
    cout << is_valid(np) << endl; 

    AlsoOk ao = static_cast<AlsoOk>(D - 1); 
    cout << is_valid(ao) << endl; 

    return 0; 
} 

Что дает выход:

1 
0 
0 

Очевидно, что выход для первого вызова is_valid, должно быть 0/ложь. Так или иначе, перечисление одновременно подписывается и без знака?

Я пропустил какую-то критическую часть документации в стандартной библиотеке относительно шаблонов, которые я использовал?

Это поправимо, выполняя сравнение, как так:

return static_cast<typename std::underlying_type<Enum>::type>(e) <= enum_traits<Enum>::LAST; 

Но это не кажется, что должно быть необходимым.

Я попробовал это на GCC 4.8.1, GCC 4.7.3 и 3.2.1 лязгом, все на x86-64

ответ

2

C++ 11 5.2.9 [expr.static.cast]/10 :

Значение интегрального или перечисляемого типа может быть явно преобразовано в тип перечисления. Значение равно без изменений, если исходное значение находится в пределах значений перечисления (7.2). В противном случае полученное значение не указывается (и может отсутствовать в этом диапазоне).

«Диапазон значений перечисления» определяется в 7.2/7:

Для перечисления, чей базовый тип фиксируется, значения перечисления являются значения базового типа. В противном случае для перечисления, где emin - наименьший перечислитель, а emax - наибольшее значение , значения перечисления являются значениями в диапазоне от bmin до bmax, определяемым следующим образом: Пусть K равно 1 для представления представления двух и 0 для представление своего дополнения или знака. bmax - наименьшее значение, большее или равное max (| emin | - K, | emax |) и равно 2M - 1, где M - неотрицательное целое число.bmin равно нулю, если emin неотрицательно и - (bmax + K) в противном случае. Размер наименьшее битовое поле, достаточно большое для хранения всех значений типа перечисления, является max (M, 1), если bmin равно ноль и M + 1 в противном случае. Можно определить перечисление, которое имеет значения, не определенные каким-либо из его нумераторов . Если переписчик-список пуст, значения перечислений являются как бы перечисление было одного нумератора со значением 0.

Для Colour, диапазон значений перечисления (предполагается, что дополнительный код дополнение) является [ 0, 3]. RED - 1 либо -1, либо UINT_MAX, оба из которых находятся за пределами диапазона [0, 3], поэтому результат static_cast не указан.

Поскольку результат преобразования значений вне диапазона не указан, вам лучше выполнить сравнения в домене базового типа, что является точно результатом вашего исправления.

Смежные вопросы