Неопределенная ссылка на статический член класса - PullRequest
187 голосов
/ 07 ноября 2008

Может кто-нибудь объяснить, почему следующий код не скомпилируется? По крайней мере, на g ++ 4.2.4.

И еще интересно, почему он будет компилироваться, когда я приведу MEMBER к int?

#include <vector>

class Foo {  
public:  
    static const int MEMBER = 1;  
};

int main(){  
    vector<int> v;  
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

Ответы [ 7 ]

190 голосов
/ 07 ноября 2008

Вам нужно где-то определить статический член (после определения класса). Попробуйте это:

class Foo { /* ... */ };

const int Foo::MEMBER;

int main() { /* ... */ }

Это должно избавить от неопределенной ссылки.

71 голосов
/ 07 ноября 2008

Проблема возникает из-за интересного столкновения новых функций C ++ и того, что вы пытаетесь сделать. Сначала давайте взглянем на подпись push_back:

void push_back(const T&)

Ожидается ссылка на объект типа T. При старой системе инициализации такой член существует. Например, следующий код компилируется просто отлично:

#include <vector>

class Foo {
public:
    static const int MEMBER;
};

const int Foo::MEMBER = 1; 

int main(){
    std::vector<int> v;
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

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

#define MEMBER 1

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

58 голосов
/ 07 ноября 2008

Стандарт C ++ требует определения вашего статического константного члена, если определение каким-то образом необходимо.

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

Когда вы явно приводите константу, вы создаете временный объект, и именно этот временный объект привязывается к ссылке (в соответствии со специальными правилами в стандарте).

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

Хотя странным образом это можно рассматривать как законное использование унарного оператора '+'. В основном результат unary + является rvalue, и поэтому применяются правила привязки rvalues ​​к ссылкам const, и мы не используем адрес нашего статического члена const:

v.push_back( +Foo::MEMBER );
10 голосов
/ 04 декабря 2012

Aaa.h

class Aaa {

protected:

    static Aaa *defaultAaa;

};

Aaa.cpp

// You must define an actual variable in your program for the static members of the classes

static Aaa *Aaa::defaultAaa;
1 голос
/ 07 ноября 2008

Не знаю, почему приведение работает, но Foo :: MEMBER не выделяется до тех пор, пока Foo не загрузится в первый раз, и, поскольку вы никогда не загружаете его, он никогда не выделяется. Если бы у вас где-то была ссылка на Foo, это, вероятно, сработало бы.

0 голосов
/ 20 июня 2016

С C ++ 11, вышеперечисленное было бы возможно для базовых типов как

class Foo {
public:  
  static constexpr int MEMBER = 1;  
};

Часть constexpr создает статическое выражение , а не статическую переменную - и это ведет себя как очень простое определение встроенного метода. Однако этот подход оказался немного шатким, когда в классах шаблонов использовались контекстеры C-строки.

0 голосов
/ 18 июля 2014

По второму вопросу: push_ref принимает ссылку в качестве параметра, и вы не можете иметь ссылку на статический константный член класса / структуры. Как только вы вызываете static_cast, создается временная переменная. И ссылку на этот объект можно передать, все работает просто отлично.

Или, по крайней мере, мой коллега, который разрешил это, сказал так.

...