Weird undefined символы статических констант внутри struct / class

Либо я очень устал, либо что-то странное, что я не знаю, потому что код ниже приводит к неопределенным символам для Foo :: A и Foo :: B при связывании . Это сводится к минимуму, насколько я мог, из более крупного проекта, но показывает суть того, что я ищу.

#include  struct Foo { static const int A = 1; static const int B = 2; }; int main() { return std::min(Foo::A, Foo::B); } 

Без шаблона функции std :: min он отлично работает , т.е. просто возвращает Foo :: A. Также прекрасно, когда вы определяете статические ints вне classа / структуры (глобальные в этом простом случае). Однако, как только они будут внутри, линкер не может их найти.

Может кто-нибудь объяснить, что происходит?

Необходимое определение

Код, который вы предоставили, является нестандартным. Хотя вы можете предоставить инициализаторы для константных статических членов int непосредственно в classе, вам все равно необходимо предоставить отдельные определения. Это странно, что-то неожиданное, но вы должны написать его вот так:

 #include  struct Foo { static const int A = 1; static const int B = 2; }; const int Foo::A; const int Foo::B; int main() { return std::min(Foo::A, Foo::B); } 

Цитата из стандарта может быть найдена в аналогичном вопросе при константных и статических спецификациях в c ++

Почему иногда код «работает» без определения?

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

Почему ваш источник попадает в категорию «компоновщик» – это то, чего я не вижу, нужно было бы разбирать std :: min, чтобы понять это. Примечание. Когда я попытался подключиться к сети с помощью GCC , он работал, ошибка не была обнаружена.

Альтернатива: используйте перечисление

Другой альтернативой является использование enums. Эта версия также может пригодиться, если вы нажмете старый компилятор, который не поддерживает инициализаторы static in int int inline (например, Visual Studio 6). Обратите внимание, однако, что при std :: min вы сталкиваетесь с другими проблемами с enumsми, и вам нужно использовать явное инстанцирование или кастинг или иметь как A, так и B в одном названном enum, как в ответе Nawaz :

 struct Foo { enum {A = 1}; enum {B = 2}; }; int main() { return std::min(Foo::A, Foo::B); } 

стандарты

Примечание: даже Stroustrup C ++ часто задает этот вопрос и не требует определения строго так, как это делает стандарт:

Вы можете взять адрес статического члена, если (и только если) он имеет определение вне classа

Определение требуется по стандарту в 9.4.2:

C ++ 03:

Член все еще должен быть определен в области пространства имен, если он используется в программе, и определение области пространства имен не должно содержать инициализатор

C ++ 11 формулировка 9.4.2 немного отличается:

3 Член все еще должен быть определен в области пространства имен, если он используется в odr (3.2) в программе

3.2 говорит следующее о применении odr:

3 Переменная x, имя которой отображается как потенциально вычисленное выражение ex, используется odr, если только x не является объектом, удовлетворяющим требованиям для отображения в константном выражении (5.19), а ex является элементом набора потенциальных результатов выражения e, где либо преобразование lvalue-to-rvalue (4.1) применяется к e, либо e является выражением отбрасываемого значения (п. 5).

4 Каждая программа должна содержать ровно одно определение каждой не-встроенной функции или переменной, которая является odr-используемой в этой программе; не требуется диагностика.

Я должен признать, что не знаю, каковы точные последствия формулировки C ++ 11, поскольку я не понимаю правила использования odr.

Если вам нужны только интегральные значения, вы также можете определить enum :

 #include  struct Foo { enum integrals { A = 1, B = 2} ; }; int main() { return std::min(Foo::A, Foo::B); } 

Этого более чем достаточно. Никакой декларации не требуется вне classа!

Демо-версия: http://www.ideone.com/oE9b5

Вы должны определить статические константы вне определения classа.

 struct Foo { static const int A; static const int B; }; const int Foo::A = 1; const int Foo::B = 2; 

Видя, как вы в основном используете struct как пространство имен, почему бы просто не использовать пространство имен:

 #include  namespace Foo { const int A = 1; const int B = 2; }; int main() { return std::min(Foo::A, Foo::B); } 

Здесь есть хорошие ответы, но еще одна вещь, которую следует отметить, заключается в том, что параметры std::min() – это ссылки, для которых требуются адреса переданных в переменных, и поскольку эти переменные не попадают в объект файл для блока компиляции, компоновщик не может разрешить свои адреса.

Вероятно, вы получаете это в не оптимизированной сборке, правильно?

Готов поспорить, что вы не получите это с помощью gcc, если вы включите оптимизацию. Вызов std::min() будет вставлен в очередь, и ссылки исчезнут.

Кроме того, если вы должны были назначить Foo::A и Foo::B двум локальным переменным непосредственно перед вызовом std::min() , эта проблема также исчезнет.

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

  • Используется ли для объявлений локальных функций?
  • Haskell: Что такое нормальная форма слабой головы?
  • Что такое «Java Bean»?
  • Что такое программирование RESTful?
  • Что такое «stream» (действительно)?
  • Что такое AJAX?
  • Что такое конечная точка веб-сервиса?
  • Что такое одно правило определения в C ++?
  • Какое определение «интерфейс» в объектно-ориентированном программировании
  • Ошибка с несколькими определениями функции
  • Определение статических константных элементов в определении classа
  • Давайте будем гением компьютера.