2008-11-07 3 views
172

Может кто-нибудь объяснить, почему следующий код не будет компилироваться? По крайней мере, на 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; 
} 
+0

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

. Это означает, что угловые скобки не интерпретируются как HTML. – 2008-11-07 19:03:01

+0

cheers :) 10 символов правило правило иногда раздражает;) – 2008-11-08 12:30:10

ответ

176

Необходимо определить статический член где-нибудь (после определения класса). Попробуйте следующее:

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

const int Foo::MEMBER; 

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

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

+3

Хорошая точка, встроенная статическая константа целочисленная инициализация создает скользящую константу целого, которую вы не можете взять по адресу, а вектор принимает ссылочный параметр. – 2008-11-07 18:08:07

+9

Этот ответ касается только первой части вопроса. Вторая часть намного интереснее: почему добавление ника NOP делает работу без необходимости внешней декларации? – nobar 2011-02-01 00:48:40

+27

Я просто потратил немало времени на выяснение того, что если определение класса находится в файле заголовка, то распределение статической переменной должно быть в файле реализации, а не в заголовке. – shanet 2012-07-14 03:06:28

69

Проблема возникает из-за интересного столкновения новых функций 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 

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

1

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

56

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

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

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

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

Хотя это странное поведение можно рассматривать как законное использование унарного оператора «+». В основном результат unary + является Rvalue и поэтому правила для связывания rvalues ​​на константные ссылки применяются, и мы не используем адрес нашего статического члена ПОСТОЯННОГО:

v.push_back(+Foo::MEMBER); 
9

Aaa.h

class Aaa { 

protected: 

    static Aaa *defaultAaa; 

}; 

Aaa.каст

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

static Aaa *Aaa::defaultAaa; 
0

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

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

1

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

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

constexpr часть создает статическую выражение в отличие от статической переменной - и ведет себя точно так же как чрезвычайно простое определение встроенного метода. Тем не менее подход оказался немного шатким с C-string constexprs внутри классов шаблонов.

Смежные вопросы