Как переопределить правило интегрального продвижения для перечисления?

Это тесно связано с вопросом Являются ли перечисления C++ подписанными или неподписанными?. Согласно ответу JavaMan, enum не является ни signed, ни unsigned. Но он следует неотъемлемым правилам продвижения.

Я работаю с библиотекой, которая использует enums, а затем передает их другим объектам класса, которые ожидают в основном типы unsigned (например, unsigned int и size_t). Включение предупреждения -Wsign-conversion в попытке выявить допустимые ошибки приводит к ряду ложных срабатываний из-за правил языка.

Правило как бы создает ситуацию, когда трудно обеспечить безопасность типов и отловить распространенные ошибки. Это сложно, потому что я хочу избежать таких вещей, как static_cast, разбросанных по всему коду.

Есть ли способ переопределить поведение языка по умолчанию для преобразования enums в конкретные типы signed или unsigned? (Подобно тому, как вы можете указать char со знаком или без знака).


Связано это с тем, что библиотека была написана в 1990-х годах, поэтому она поддерживает несколько старых компиляторов. Было бы здорово, если бы решение обращалось даже к C++03 и, возможно, раньше.

Из Как защитить конструкторы перемещения для C++03 и C++11? я знаю, что на практике нет надежного способа обнаружить когда действуют другие варианты языка C++. Во время тестирования Clang 3.5 с использованием -std=c++03 и -std=c++11 он рухнул на землю.


person jww    schedule 06.07.2015    source источник
comment
Можете ли вы использовать С++ 11 и можете ли вы изменить определения перечисления в этой библиотеке?   -  person Praetorian    schedule 06.07.2015
comment
@Praetorian - К сожалению, нет, мы не можем зависеть от С++ 11. Нам все еще нужно поддерживать C++03 (и, возможно, раньше). С другой стороны, я могу изменить определения перечисления в библиотеке. При необходимости можно использовать библиотеку Crypto++ Вей Дая. Извините, что не предоставили эту информацию раньше.   -  person jww    schedule 06.07.2015


Ответы (2)


Базовый тип перечисления C++03 зависит от диапазона значений его перечислителей и продвигается к базовому типу, а не к int (C++98 [conv.prom] §4.5/2).

Грязный способ заставить перечисление вести себя как unsigned int — это добавить значение, которое может обработать только unsigned int.

enum things {
    a, b, c,
    force_unsigned = -1U
};

Демонстрация: http://coliru.stacked-crooked.com/a/d3ded108fb5a68bf

person Potatoswatter    schedule 06.07.2015
comment
пришлось почесать голову, к чему приведет -1u, это помогло мне: brnz.org/hbr/ ?p=1433 - person user2950911; 06.07.2015
comment
@ user2950911 Да, и важно, чтобы -1u было положительным числом. Чтобы это работало, все значения перечислителя должны быть положительными, иначе компилятору придется выбрать signed long или signed long long, которые могут содержать как -1u, так и правильные отрицательные числа. - person Potatoswatter; 06.07.2015

Вы можете свернуть свой собственный enum class:

struct safe_enum {
    enum type {
        value1, value2, value3
    };
    type value;

    operator unsigned int ()
        { return value; }
};

safe_enum foo = safe_enum::value1;
unsigned bar = safe_enum::value2;

К сожалению, при этом теряется «неограниченное» поведение перечислений C++03, поэтому этот шаблон передовой практики нарушит кодовую базу. Кроме того, изменение enum на class приведет к поломке ABI, если он был отправлен в виде DLL.

person Potatoswatter    schedule 06.07.2015