Неопределенная ссылка на статический член класса

StackOverflow https://stackoverflow.com/questions/272900

  •  07-07-2019
  •  | 
  •  

Вопрос

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

Решение

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

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

const int Foo::MEMBER;

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

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

Другие советы

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

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

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

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

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

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

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

v.push_back( +Foo::MEMBER );

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;

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top