2011-12-19 5 views
1

// SomeCls.hстатические Интс, единицы компиляции и тройной оператор

class SomeCls 
    { 
    static const int PERIOD_ALARM_NORMAL = 5;   
    static const int PERIOD_ALARM_THRESH = 1;   

    void method() 
    { 
     bool b = true; 
     const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL; 
    } 

    } obj; 

Он собирается построить ок. Теперь выньте реализацию метода() и поместите его в файл cpp:

//SomeCls.cpp 
#include "SomeCls.h" 

void SomeCls::method() 
    { 
     bool b = true; 
     const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL; 
    } 

Почему mr. линкер сказать

неопределенная ссылка на SomeCls::PERIOD_ALARM_NORMAL' undefined reference to SomeCls :: PERIOD_ALARM_THRESH»

?

Благодаря

EDIT: Мне кажется, что это в .h, тройной оператор принимает статическую сопзЬ INTS как rvalues ​​но ... вне decalrative .h, он рассматривает их как Lvalue и требует определения , Вот что я понял из ответов ниже. Престижность Bada компилятора (некоторые EABI линукс thinggie)

+0

Почему линкер говорит, что когда вы делаете * what *? Что вы пытаетесь точно связать? –

+0

@Scarlet: Я думаю, вы что-то не понимаете. OP не хочет тестировать 'd == b', он хочет назначить' d', тестирование на 'b'. – Xeo

+0

Пожалуйста, не используйте all-caps для имен переменных. Они традиционно используются только для макросов препроцессора (кроме того, что они очень утомительны для чтения). –

ответ

2

Это ограничение GCC, но оно полностью стандартно. Технически static const int по-прежнему является lvalue. Вы предоставили значение inline, поэтому компилятор почти всегда будет использовать его как rvalue. Есть одно исключение. Абстрактные инструкции, испускаемые компилятором для тернарных операторов, запрашивают адрес lvalues. Отсюда ошибка, которую вы видите.

Вы можете обойти это, используя вместо этого enum. Или если вы используете новую версию GCC constexpr, она была добавлена ​​к стандарту, чтобы исправить эту точную проблему (названные и напечатанные rvalues).

В качестве альтернативы вы можете предоставить компоновщик с определением констант. Например. в файле классов CPP добавить строку типа

// I wish I had constexpr 
const int SomeCls::PERIOD_ALARM_NORMAL; 
const int SomeCls::PERIOD_ALARM_THRESH; 

В качестве примечания: Я был ярым сторонником static const для области видимости класса констант. Затем я узнал, что MSVC не позволяет использовать static const float со значением inline.Таким образом, единственными значениями, которые вы можете поместить в static const, являются целые числа, и в этом случае enum s предоставляют все те же функции плюс гарантия того, что они никогда не будут автоматически конвертировать в lvalue.

+0

, который был простым. Я просто надеюсь, что вы правы с объяснением lvalue/rvalue и что я выбираю наиболее точный ответ: P. Изменить: похоже, это не точно. Это не объясняет, как это работает в первом сценарии - все в .h – kellogs

+0

Встроенные инициализации нецелых типов разрешены только для 'static constexpr' в C++ 11. Это не имеет ничего общего с MSVC ... –

+0

Обратите внимание, что это только стандарт, соответствующий предыдущему стандарту C++ 03. В C++ 11, формулировка ODR изменилась: «§3.2 [basic.def.odr] p2'» [...] Переменная, имя которой отображается как потенциально оцениваемое выражение, является * odr-used *, если оно не является объект, который удовлетворяет требованиям для появления в постоянном выражении (5.19) [...] ". Я бы очень сказал, что 'static const int' * does * удовлетворяет указанным требованиям. – Xeo

3

Если компилятор не может видеть все константы статического класса значения, то вы должны дать определения для них, так что они на самом деле где-то хранить. Добавьте следующие строки в файл CPP:

const int SomeCls::PERIOD_ALARM_NORMAL; 
const int SomeCls::PERIOD_ALARM_THRESH; 
+0

+1, я не знал о том, что «если он не может видеть свои ценности», часть –

+0

@SethCarnegie: Ну, возможно, это будет наоборот ... »в хороших случаях * вам может не понадобиться определение * потому что компилятор уже знает значения и может сворачивать их в код, не требуя фактической переменной «... Я полагаю, что определение функции вне очереди является достаточной причиной для того, чтобы компилятор настаивал на определении. –

+0

Фактические определения в блоке перевода должны быть необходимы только в том случае, если он принимает * адрес * этих переменных в любом месте. Он этого не делает, поэтому пример, который он дал, должен составлять отлично. – Xeo

1

Если по какой-либо причине, вы компилятор просто отказывается связать код (например, GCC 4.4.5 делает), вот простое решение проблемы: заменить static const int с с enum ,

// someclass.h 
// include guards, blabla 

class SomeClass 
{ 
    enum AlarmPeriod{ 
     PERIOD_ALARM_NORMAL = 5, 
     PERIOD_ALARM_THRESH = 1 
    };  

public: 
    void method(); 
}; 

// someclass.cpp 
#include "someclass.h" 

void SomeClass::method(){ 
    bool b = true; 
    const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL; 
} 

// main.cpp 
#include "someclass.h" 

int main(){ 
    someclass sc; 
    sc.method(); 
} 

Это связывает чисто с GCC 4.4.5, которые не связывают предыдущую версию, хотя и технически одинаковы.

Обратите внимание, что вы не можете, между прочим, брать адреса PERIOD_ALARM_NORMAL и PERIOD_ALARM_TRESH, потому что оба имени - просто псевдонимы для соответствующих значений.

+0

Но тогда константы имеют четкий, неназванный тип. – curiousguy

+0

@ curiousguy: Счастлив? Как будто это изменит пример, показанный OP. – Xeo

+0

В этом случае это может не повлиять, но разница между «enum» и «static const int» может влиять на код на языке C++, и это может быть удивительно. Я думаю, это стоит упомянуть. – curiousguy

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