2011-08-16 3 views
1

Я читал интро на GTEST и нашел эту часть запутанной:Почему это «недопустимый C++»

Компилятор жалуется на «неопределенные ссылки» на некоторых статических переменных членов Const, но я их определить в классе. Что случилось?

Если ваш класс имеет статический член данных:

// foo.h 
class Foo { 
    ... 
    static const int kBar = 100; 
}; 

Вы также должны определить его вне тела класса в foo.cc:

const int Foo::kBar; // No initializer here. 

В противном случае ваш код недействительным C++, и может неожиданно прервать способов. В частности, использование его в тестах сравнения Google Test (EXPECT_EQ и т. Д.) Приведет к возникновению ошибки «неопределенной ссылки».

Может кто-нибудь объяснить, почему определение статической константы в классе без определения ее вне класса тела является незаконным C++?

+0

Очевидное определение в классе класса формально не является * определением *, это просто * объявление *. достаточно использовать значение, но не для использования объекта. в стандарте C++ 98 были проблемы (в ODR), о том, когда что-то было «использовано», но в основном оно исправлено в C++ 0x. –

+0

Вам нужно переслать объявление статических переменных в исходный файл (.cpp), если будут какие-либо ссылки на него. – AJG85

ответ

5

Прежде всего, внутри тела класса не является определением, это декларация. Объявление определяет тип и значение константы, определение резервирует пространство для хранения. Возможно, вам не понадобится место для хранения, например, если вы используете значение только как константу времени компиляции. В этом случае ваш код является совершенно законным C++. Но если вы сделаете что-то вроде передачи константы по ссылке или указате точку указателя на константу, вам также понадобится хранилище. В этих случаях вы получите ошибку «неопределенной ссылки».

+0

«Определение определяет тип и значение константы, определение резервирует пространство для хранения». Typo? – NoSenseEtAl

+0

@NoSenseEtAl: Да, исправлено – john

3

Стандарт в основном утверждает, что даже если вы можете указать значение в заголовке, если статическая переменная используется «0», должен все еще определить ее в исходном файле.

В этом контексте термин «используемый» обычно понимается как означающий, что для некоторой части программы требуется фактическая память и/или адрес переменной.

Скорее всего, тестовый код google принимает адрес переменной в какой-то момент (или использует ее каким-либо другим эквивалентным способом).

+0

Неужели некоторые компиляторы позволяют вам уйти, не делая этого? Я не помню, чтобы когда-либо помещал другое объявление за пределы класса в исходный файл, и у меня не было никаких ошибок (в MSVC++). –

+1

@Seth Carnegie: Все зависит от того, используете ли вы значение или нет. Это немного дошло до меня сегодня, интересно. Код 'struct test {static const int x = 10; }; void foo (int const & x) {std :: cout << x; } int main() {std :: cout << test :: x; foo (test :: x); } 'показывает два разных применения статической константы.В выражении 'cout << test :: x', разрешение перегрузки находит' ostream :: operator << (int) 'как лучшую перегрузку и требует только rvalue, поэтому наличие только объявления с инициализацией прекрасное. С другой стороны, для вызова 'foo' требуется lvalue (привязка к ссылке). –

+1

... для привязки ссылки вам нужно lvalue, а это означает, что статический член * используется * в соответствии со стандартом, а тот факт, что он используется, означает, что определение требуется. Если вы думаете об этом, привязка ссылки * требует * местоположения члена, и это местоположение нуждается в определении. В случае rvalue адрес не нужен, но только * значение *, и компилятор может вставлять константу на место (т. Е. В приведенном выше примере компилятор заменит 'test :: x' на' 10') –

2

Грубо: в определении класса static const int kBar = 100; сообщает компилятору, что «Foo будет иметь константу kBar (которой я обещаю всегда 100)». Однако компилятор пока не знает, где эта переменная. В файле foo.cc файл const int Foo::kBar; сообщает компилятору «Хорошо, сделайте kBar в этом месте». В противном случае компоновщик ищет kBar, но не может найти его нигде.

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