Интегральные константы C++ + оператор выбора = проблема!

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

#include <iostream>
using std::cin;
using std::cout;

class MagicNumbers
{
public:
  static const int BIG = 100;
  static const int SMALL = 10;
};

int main()
{
  int choice;
  cout << "How much stuff do you want?\n";
  cin >> choice;
  int stuff = (choice < 20) ? MagicNumbers::SMALL : MagicNumbers::BIG; // PROBLEM!
  cout << "You got " << stuff << "\n";
  return 0;
}

Я получаю ошибки ссылки в gcc 4.1.2 при компиляции с -O0 или -O1, но все нормально при компиляции с -O2 или -O3. Он хорошо работает с использованием MS Visual Studio 2005 независимо от параметров оптимизации.

test.cpp:(. text + 0xab): неопределенная ссылка на `MagicNumbers:: SMALL '

test.cpp:(. text + 0xb3): неопределенная ссылка на MagicNumbers::BIG

Я посмотрел на код промежуточной сборки, и да, неоптимизированный код рассматривал SMALL и BIG как внешние переменные int, тогда как оптимизированный использовал фактические числа. Каждое из следующих изменений устраняет проблему:

  • Используйте enum вместо int для констант: enum {SMALL = 10}

  • Приведите константу (любую) при каждом использовании: (int)MagicNumbers::SMALL или же (int)MagicNumbers::BIG или даже MagicNumbers::SMALL + 0

  • Используйте макрос: #define SMALL 10

  • Не используйте оператор выбора: if (choice < 20) stuff = MagicNumbers::SMALL; else stuff = MagicNumbers::BIG;

Мне больше нравится первый вариант (однако, он не идеален, потому что мы на самом деле используем uint32_t вместо int для этих констант, а enum является синонимом int). Но я действительно хочу спросить: чья это ошибка?

Я виноват в том, что не понял, как работают статические интегральные константы?

Должен ли я обвинять gcc и надеяться на исправление (или, возможно, в последней версии уже есть исправление, или, может быть, есть неясный аргумент командной строки, чтобы заставить это работать)?

Между тем, я просто компилирую свой код с оптимизацией, и отладка становится трудной:-O3

8 ответов

Решение

Несмотря на общепринятый совет, я обнаружил, что static const int ... неизменно дает мне больше головной боли, чем старый добрый enum { BIG = 100, SMALL = 10 };, И с C++11, обеспечивающим строго типизированные перечисления, у меня теперь есть еще меньше причин использовать static const int ...,

Это известная проблема. Стандарт виноват или вы не предоставили определение статики. В зависимости от вашей точки зрения:)

Статические члены-данные не работают так в C++:

Статические члены-данные не являются частью объектов данного типа класса; они являются отдельными объектами. В результате объявление статического члена данных не считается определением. Член данных объявляется в области видимости класса, но определение выполняется в области видимости файла. Эти статические члены имеют внешнюю связь.

Вы только объявляете эти константы, даже если вы их инициализируете. Вы все еще должны определить их в области имен:

class MagicNumbers
{
public:
    static const int BIG = 100;
    static const int SMALL = 10;
};

const int MagicNumbers::BIG;
const int MagicNumbers::SMALL;

Это избавит от ошибок ссылок.

Хех, в соответствии со стандартом C++, 9.4.2 (class.static.data):

Если член статических данных имеет константный литеральный тип, его объявление в определении класса может указывать инициализатор скобок или равных, в котором каждое предложение инициализатора, являющееся выражением присваивания, является константным выражением. Статический член данных литерального типа может быть объявлен в определении класса с помощью спецификатора constexpr; если это так, его объявление должно указывать инициализатор скобок или равных, в котором каждое предложение инициализатора, являющееся выражением присваивания, является константным выражением. [Примечание: в обоих этих случаях член может появляться в константных выражениях. - примечание конца] Элемент все еще должен быть определен в области пространства имен, если он используется в программе, и определение области пространства имен не должно содержать инициализатор.

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

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

class MagicNumbers
{
public:
  static const int BIG;
  static const int SMALL;
};

const int MagicNumbers::BIG = 100;
const int MagicNumbers::SMALL = 10;

Более высокие уровни оптимизации, вероятно, включают уровень статического анализа, достаточно тщательный, чтобы определить, что BIG а также SMALL можно заменить их фактическими значениями и не предоставлять им никакого фактического хранилища (семантика будет той же самой), поэтому определение этих переменных в этом случае будет избыточным, следовательно, это связывает ОК.

Мне было бы трудно утверждать, что это чья-то ошибка.

Статические константные интегралы, заданные значения в точке объявления, не являются переменными, они являются константными выражениями. Чтобы существовала переменная, вам все равно нужно ее определить.

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

Вам все еще нужно выделить место для них где-нибудь:

class MagicNumbers
{
public:
  static const int BIG = 100;
  static const int SMALL = 10;
};
const int MagicNumbers::BIG;
const int MagicNumbers::SMALL;

Почему ваши магические числа в классе?

namespace MagicNumbers {
    const int BIG = 100;
    const int SMALL = 10;
}

Проблема решена, не беспокоясь о недостатках в стандарте C++.

Другие вопросы по тегам