2009-10-28 2 views
47

Я хотел бы определить константу char * в моем заголовочном файле для моего .cpp-файла для использования. Так что я попытался это:Как объявить статический const char * в вашем файле заголовка?

private: 
    static const char *SOMETHING = "sommething"; 

Который приносит мне следующую ошибку компиляции:

error C2864: 'SomeClass::SOMETHING' : only static const integral data members can be initialized within a class

Я новичок в C++. Что здесь происходит? Почему это незаконно? И как вы можете это сделать в качестве альтернативы?

+1

Вы должны использовать «статический Const символ * константный НЕЧТО» вместо того, чтобы, если вы действительно хотите иметь возможность переназначить SOMETHING, чтобы указать на что-то еще во время выполнения. – bk1e

ответ

57

Вам необходимо определить статические переменные в единицах перевода, если они не являются интегральными типами.

В вашем заголовке:

private: 
    static const char *SOMETHING; 
    static const int MyInt = 8; // would be ok 

В файле .cpp:

const char *YourClass::SOMETHING = "something"; 

C++ стандартный, 9.4.2/4:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression. In that case, the member can appear in integral constant expressions within its scope. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

+0

, поэтому мне невозможно назначить это в файле заголовка? Я могу сделать это с другими типами данных, такими как DWORD. Почему это? – Mark

+3

Поскольку указатель не является интегральным типом. Интеграл включает только встроенные модули. –

+0

Почему это так? Какое ограничение ограничило это ограничение на C++? – Mark

15

Ошибка в том, что вы не можете инициализировать static const char* в классе. Вы можете инициализировать здесь целые переменные.

Вы должны объявить переменную-член в классе, а затем инициализировать его вне класса:

// заголовок файла

class Foo { 
    static const char *SOMETHING; 
    // rest of class 
}; 

// CPP файл

const char *Foo::SOMETHING = "sommething"; 

Если это кажется раздражающим, думайте об этом как о том, что инициализация может появляться только в одной единицы перевода. Если это было в определении класса, это обычно включалось бы несколькими файлами. Константные целые числа - это особый случай (что означает, что сообщение об ошибке, возможно, не так ясно, как могло бы быть), а компиляторы могут эффективно заменить использование переменной с целым значением.

Напротив, переменная char* указывает на фактический объект в памяти, который необходим для действительно существующего, и это определение (включая инициализацию), которое делает объект существующим. «Одно правило определения» означает, что вы не хотите помещать его в заголовок, потому что тогда все единицы перевода, включая этот заголовок, будут содержать определение. Они не могут быть связаны друг с другом, хотя строка содержит одинаковые символы в обоих, потому что в текущих правилах C++ вы определили два разных объекта с тем же именем, и это не является законным. Тот факт, что у них в них одинаковые персонажи, не делает его законным.

+0

Вам не нужен другой «статический» в файле cpp. – VictorCreator

1

Константный инициализатор, разрешенный C++ Стандартный только для интегральных или перечисляемых типов. См 9.4.2/4 Подробности:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a name- space scope if it is used in the program and the namespace scope definition shall not contain an initializer.

И 9.4.2/7:

Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3).

Таким образом, вы должны написать где-нибудь в CPP файле:

const char* SomeClass::SOMETHING = "sommething"; 
3

Если вы используя Visual C++, вы можете без него сделать это, используя подсказки для компоновщика ...

// In foo.h... 

class Foo 
{ 
public: 
    static const char *Bar; 
}; 

// Still in foo.h; doesn't need to be in a .cpp file... 

__declspec(selectany) 
const char *Foo::Bar = "Blah"; 

__declspec(selectany) означает, что даже если Foo::Bar получит объявленный в нескольких объектных файлах, компоновщик будет только подобрать один.

Имейте в виду, что это будет работать только с инструментальной цепочкой Microsoft. Не ожидайте, что это будет переносной.

3

Существует трюк, который вы можете использовать с шаблонами, чтобы предоставить только H-константы.

(заметьте, это некрасиво пример, но работает дословно, по крайней мере, в г ++ 4.6.1.)

(values.hpp файл)

#include <string> 

template<int dummy> 
class tValues 
{ 
public: 
    static const char* myValue; 
}; 

template <int dummy> const char* tValues<dummy>::myValue = "This is a value"; 

typedef tValues<0> Values; 

std::string otherCompUnit(); // test from other compilation unit 

(main.cpp)

#include <iostream> 
#include "values.hpp" 

int main() 
{ 
    std::cout << "from main: " << Values::myValue << std::endl; 
    std::cout << "from other: " << otherCompUnit() << std::endl; 
} 

(other.cpp)

#include "values.hpp" 

std::string otherCompUnit() { 
    return std::string(Values::myValue); 
} 

Компиляция (например, г ++ -o главный main.cpp other.cpp & & ./main) и посмотреть две единицы компиляции, ссылающийся на ту же константу объявленную в заголовке:

from main: This is a value 
from other: This is a value 

В MSVC, вы можете вместо того, чтобы быть в состоянии использовать __declspec (selectany)

Например:

__declspec(selectany) const char* data = "My data"; 
+0

Правильный трюк неверен. Невозможно предоставить несколько определений явной специализации. Это может сработать, если компилятор не сможет его поймать, но это нарушение ODR. – AnT

+0

На самом деле, правильная реализация шаблонного трюка будет использовать неспециализированную декларацию. – AnT

+1

Как раз другой случай, когда компилятор MS не соответствует всем стандартам? Должен признаться, я никогда не пробовал это на GCC. – Torlack

24

Чтобы ответить на вопрос OP о том, почему это разрешено только с целыми типами.

Когда объект используется как lvalue (то есть как что-то, что имеет адрес в хранилище), он должен удовлетворять «одному правилу определения» (ODR), то есть он должен быть определен в одном и только одном блоке перевода , Компилятор не может и не будет решать, какую единицу перевода указать для этого объекта. Это ваша ответственность. В , определяющем этот объект, где вы не просто определяете его, вы на самом деле говорите компилятору, что вы хотите его определить. здесь, в это определенная единица перевода.

Между тем, на языке C++ интегральные константы имеют особый статус. Они могут формировать интегральные постоянные выражения (ICE). В ICE интегральные константы используются как обычные значения, а не как объекты (то есть не имеет значения, имеет ли такое интегральное значение адрес в хранилище или нет). Фактически, ICE оцениваются во время компиляции. Чтобы облегчить такое использование интегральных констант, их значения должны быть видны глобально. И сама константа действительно не нуждается в фактическом месте в хранилище. Из-за этого интегральные константы получили специальное обращение: в заголовочный файл было разрешено включать их инициализаторы, и требование о предоставлении определения было ослаблено (сначала де-факто, затем де-юре).

Другие постоянные типы не имеют таких свойств. Другие постоянные типы практически всегда используются как lvalues ​​(или, по крайней мере, не могут участвовать в ICE или что-то подобное ICE), что означает, что они требуют определения. Остальное следует.

+0

кроме шаблонов –

+1

@Mooing Duck: Полный текст ODR довольно обширен. В нем много «исключений» и «исключений», которые даже не дистанционно ограничены «шаблонами». Мой ответ ограничен одной небольшой частью ODR, которая имеет дело с интегральными константами. Я не понимаю, как здесь может быть важно ваше замечание «кроме шаблонов» (я даже не понимаю, что именно вы подразумеваете под этим замечанием.) – AnT

+1

Это все правда, и я дал +1, потому что это отличный ответ, но классы шаблонов являются (насколько мне известно) исключениями для всех правил ODR. Я просто подумал, что брошу это туда. –

0

Ответ на вопрос Почему вопросы являются неотъемлемыми типами, поскольку они не являются ссылкой на выделенный объект, а являются значениями, которые дублируются и копируются. Это просто решение о внедрении, когда был определен язык, который должен обрабатывать ценности вне объектной системы и как можно более эффективные и «встроенные».

Это не совсем объясняет, почему они разрешены как инициализаторы в типе, но думают об этом как по существу #define, а затем это будет иметь смысл как часть типа, а не части объекта.

9
class A{ 
public: 
    static const char* SOMETHING() { return "something"; } 
}; 

Я делаю это все время - особенно для дорогих стандартных параметров по умолчанию.

class A{ 
    static 
    const expensive_to_construct& 
    default_expensive_to_construct(){ 
     static const expensive_to_construct xp2c(whatever is needed); 
     return xp2c; 
    } 
}; 
6

С C++ 11 вы можете использовать constexpr ключевое слово и написать в своем заголовке:

private: 
    static constexpr const char* SOMETHING = "something"; 


Примечания:

  • constexpr делает SOMETHING постоянным указателем поэтому вы не можете писать

    SOMETHING = "something different"; 
    

    далее.

  • В зависимости от компилятора, вы, возможно, потребуется написать явное определение в файле .cpp:

    constexpr const char* MyClass::SOMETHING; 
    
Смежные вопросы