Странные неопределенные символы статических констант внутри структуры / класса

c++ initialization definition static-members undefined-symbol

9940 просмотра

5 ответа

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

#include <algorithm>

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. Также хорошо при определении статических целых вне класса / структуры (глобальная в этом простом случае). Однако, как только они окажутся внутри, компоновщик не сможет их найти.

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

Автор: murrekatt Источник Размещён: 12.11.2019 09:49

Ответы (5)


42 плюса

Решение

Требуется определение

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

#include <algorithm>

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);
}

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

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

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

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

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

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

struct Foo
{
    enum {A = 1};
    enum {B = 2};
};

int main()
{
    return std::min<int>(Foo::A, Foo::B);
}

стандарты

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

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

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

Формулировка C ++ 03:

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

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

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

3.2 говорит следующее о odr-use:

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

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

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

Автор: Suma Размещён: 03.02.2011 08:07

3 плюса

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

#include <algorithm>

struct Foo
{
    enum integrals { A = 1, B = 2} ;
};

int main()
{
    return std::min(Foo::A, Foo::B);
}

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

Демонстрация в Интернете: http://www.ideone.com/oE9b5

Автор: Nawaz Размещён: 03.02.2011 08:14

2 плюса

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

struct Foo {
    static const int A;
    static const int B;
};

const int Foo::A = 1;
const int Foo::B = 2;
Автор: wilhelmtell Размещён: 03.02.2011 08:06

2 плюса

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

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

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

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

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

Автор: stingoh Размещён: 12.07.2012 07:53

1 плюс

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

#include <algorithm>

namespace Foo
{
    const int A = 1;
    const int B = 2;
};

int main()
{
    return std::min(Foo::A, Foo::B);
}
Автор: John Ripley Размещён: 04.02.2011 02:18
Вопросы из категории :
32x32