Интегральные константы C ++ + оператор выбора = проблема! - PullRequest
9 голосов
/ 18 ноября 2010

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

#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 *

.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 ]

20 голосов
/ 18 ноября 2010

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

7 голосов
/ 18 ноября 2010

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

7 голосов
/ 18 ноября 2010

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

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

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

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

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

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

3 голосов
/ 18 ноября 2010

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

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

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

1 голос
/ 18 ноября 2010

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

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

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

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

1 голос
/ 18 ноября 2010

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

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

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

0 голосов
/ 18 ноября 2010

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

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

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

0 голосов
/ 18 ноября 2010

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

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