Перечисление C ++ 11 с членами класса и оптимизацией времени компоновки constexpr

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

Насколько мне известно, это невозможно сделать со стандартным классом перечисления MyItem {...}, поэтому для каждого класса перечисления в моем проекте у меня есть вспомогательный класс MyItemEnum, который инкапсулирует эти вспомогательные статические методы, а также создает экземпляры вспомогательных экземпляров сам, чтобы я мог получить доступ к их методам для получения дополнительных атрибутов.

Ниже приведен пример (максимально упрощенный, но я считаю, что все обсуждаемые функции остались там).

MyItem.h

enum class MyItem : unsigned int {
    Item1   = 1,
    Item2   = 5
};

class MyItemEnum {
private:
    MyItem myItem;
    size_t extInfo;

    MyItemEnum(const MyItem& myItem, size_t extInfo);
    ~MyItemEnum();
public:
    static MyItemEnum Item1;
    static MyItemEnum Item2;
    static const MyItemEnum &get(MyItem myItem);

    operator MyItem() const;
    size_t getExt() const;
    bool hasNext() const;
    MyItem next() const;
};

Я думаю, что смысл очевиден, и мне не нужно указывать здесь часть .cpp ... Я использую MyItem в качестве аргумента для передачи в интерфейсах и MyItemEnum, когда мне нужно получить доступ к расширенным функциям.

Мой первый вопрос: подходит ли описанный выше подход или мне следует рассмотреть что-то совершенно другое?

Мой второй вопрос касается оптимизации этого перечисления, которое я пытаюсь выполнить с помощью constexpr:

enum class MyItem : unsigned int {
    Item1   = 1,
    Item2   = 5
};

class MyItemEnum {
private:
    MyItem myItem;
    size_t extInfo;

    constexpr MyItemEnum(const MyItem& myItem, size_t extInfo);
public:
    static MyItemEnum Item1;
    static MyItemEnum Item2;
    static constexpr MyItemEnum &get(MyItem myItem);

    constexpr operator MyItem();
    constexpr size_t getExt();
    constexpr bool hasNext();
    constexpr MyItem next();
};

Он компилируется, но, по-видимому, constexpr не может быть использован, потому что если я получу доступ:

MyItemEnum::Item1.getExt()

поэтому компилятор не знает, с какими значениями был создан экземпляр Item1. Есть ли вероятность, что приведенное выше выражение будет оценено как constexpr во время оптимизации времени ссылки? В качестве альтернативы я мог бы использовать

static constexpr MyItemEnum Item1 = MyItemEnum(MyItem::Item1, 123);

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


person David L.    schedule 30.12.2012    source источник
comment
Я думаю, вам следует разбить это на отдельные вопросы.   -  person Yochai Timmer    schedule 30.12.2012


Ответы (1)


У меня пока нет прямого опыта использования constexpr и итоговых оптимизаций компилятора, но я могу сказать вам, что простое использование const для членов самого класса или экземпляров заставит компиляторы VS2012 и g ++ 4.7 выполнять кросс-модульную оптимизацию. :

class MyItemEnum {
private:
    // make sure to put const here...
    const MyItem myItem;
    const size_t extInfo;

    MyItemEnum(const MyItem& myItem, size_t extInfo);
    ~MyItemEnum();
public:
    // and put const in here too...
    static const MyItemEnum Item1;
    static const MyItemEnum Item2;
};

Предостережение заключается в том, что конструктор должен использовать синтаксис списка инициализаторов стиля C ++, что не должно быть проблемой, если вы все равно просто заполняете их постоянными значениями. (списки инициализаторов становятся проблемой только тогда, когда требуется нетривиальная настройка).

Я не проверял это на Clang / LLVM, поэтому, если это ваша инструментальная цепочка, я настоятельно рекомендую вам взять этот упрощенный пример и самостоятельно разобрать результат. Дизассемблирование простых тестовых примеров довольно легко проанализировать, даже если вы не знакомы с языками ассемблера. И в этом случае вы можете скомпилировать две сборки: одну в одном модуле, а другую разделить на два модуля - и сравнить результаты, чтобы убедиться, что LTO выполняет нужную вам работу.

person jstine    schedule 30.12.2012