Как объявить статический const char* в вашем заголовочном файле?
Вопрос
Я бы хотел определить постоянный символ * в моем заголовочном файле для использования моего .cpp-файла.Итак, я попробовал это:
private:
static const char *SOMETHING = "sommething";
Что приводит меня к следующей ошибке компилятора:
ошибка C2864:'SomeClass::ЧТО-ТО' :только статические константные интегральные сведения члены могут быть инициализированы класс
Я новичок в C ++.Что здесь вообще происходит?Почему это незаконно?И как вы можете сделать это альтернативно?
Решение
Вам нужно определить статические переменные в единице перевода, если они не являются целочисленными типами.
В вашем заголовке:
private:
static const char *SOMETHING;
static const int MyInt = 8; // would be ok
В файле .cpp:
const char *YourClass::SOMETHING = "something";
Стандарт C ++, 9.4.2 / 4:
Если статический элемент данных имеет постоянный целочисленный или константный тип перечисления, его объявление в классе определение может указать инициализатор констант, который должен быть интегральное постоянное выражение. В этом случай, член может появиться в интегральные константные выражения внутри его сфера применения. Член все еще должен быть определяется в области имен пространства, если это используется в программе и пространстве имен определение объема не должно содержать инициализатор.
Другие советы
Чтобы ответить на вопрос ОП о том, почему он разрешен только для целочисленных типов.
Когда объект используется в качестве l-значения (то есть как то, что имеет адрес в хранилище), он должен удовлетворять «одному правилу определения». (ODR), т.е. он должен быть определен в одной и только одной единице перевода. Компилятор не может и не будет решать, в каком модуле перевода будет определен этот объект. Это ваша ответственность. определяя этот объект где-то, вы не просто определяете его, вы фактически указываете компилятору, что вы хотите определить его здесь , в этом конкретном переводческий блок.
Между тем, в языке C ++ интегральные константы имеют особый статус. Они могут формировать интегральные константные выражения (ICE). В ICE интегральные константы используются как обычные значения , а не как объекты (т.е. не имеет значения, имеет ли такое интегральное значение адрес в хранилище или нет). На самом деле, ICE оцениваются во время компиляции. Чтобы облегчить такое использование интегральных констант, их значения должны быть видны во всем мире. И сама константа не нуждается в реальном месте в хранилище. Из-за этого интегральные константы получили специальную обработку: было разрешено включать их инициализаторы в заголовочный файл, и требование предоставить определение было смягчено (сначала де-факто, а затем де-юре).
Другие типы констант не имеют таких свойств. Другие постоянные типы практически всегда используются в качестве l-значений (или, по крайней мере, не могут участвовать в ICE или в чем-либо подобном ICE), что означает, что они требуют определения. Остальное следует.
Ошибка в том, что вы не можете инициализировать static const char *
внутри класса. Там вы можете только инициализировать целочисленные переменные.
Вам необходимо объявить переменную-член в классе, а затем инициализировать ее вне класса:
// заголовочный файл
class Foo {
static const char *SOMETHING;
// rest of class
};
// файл cpp
const char *Foo::SOMETHING = "sommething";
Если это кажется раздражающим, подумайте об этом, потому что инициализация может появиться только в одной единице перевода. Если бы это было в определении класса, это обычно включало бы несколько файлов. Постоянные целые числа являются особым случаем (что означает, что сообщение об ошибке, возможно, не так ясно, как могло бы быть), и компиляторы могут эффективно заменить использование переменной целочисленным значением.
Напротив, переменная char *
указывает на реальный объект в памяти, который необходим для реального существования, и именно определение (включая инициализацию) делает этот объект существующим. «Правило одного определения» значит, вы не хотите помещать его в заголовок, потому что тогда все единицы перевода, включая этот заголовок, будут содержать определение. Они не могут быть связаны вместе, даже если строка содержит одинаковые символы в обоих, потому что в соответствии с текущими правилами C ++ вы определили два разных объекта с одним и тем же именем, и это недопустимо. Тот факт, что они содержат одинаковые символы, не делает его законным.
С C ++ 11 вы можете использовать constexpr
ключевое слово и напишите в своем заголовке:
private:
static constexpr const char* SOMETHING = "something";
Примечания:
constexpr
делаетSOMETHING
постоянный указатель, поэтому вы не можете записатьSOMETHING = "something different";
позже.
В зависимости от вашего компилятора вам также может потребоваться написать явное определение в файле .cpp:
constexpr const char* MyClass::SOMETHING;
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;
}
};
Если вы используете 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. Не ожидайте, что это будет переносимым.
Существует трюк, который вы можете использовать с шаблонами, чтобы предоставлять константы только для H-файла.
(обратите внимание, это уродливый пример, но он работает дословно, по крайней мере, в g ++ 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);
}
Скомпилировать (например,g ++ -o main 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";
Инициализатор констант допускается стандартом C ++ только для целочисленных или перечислимых типов. Подробности см. В 9.4.2 / 4:
Если статический член данных имеет константный интеграл или константный тип перечисления, его объявление в классе В определении можно указать инициализатор константы, который должен быть выражением интегральной константы (5.19). В этом В этом случае член может появляться в виде интегральных константных выражений. Член по-прежнему должен быть определен в область действия, если она используется в программе, а определение области действия пространства имен не должно содержать инициализатор.
И 9.4.2 / 7:
Статические члены данных инициализируются и уничтожаются точно так же, как нелокальные объекты (3.6.2, 3.6.3).
Так что вы должны написать где-нибудь в файле cpp:
const char* SomeClass::SOMETHING = "sommething";
Чтобы ответить на вопрос почему , целочисленные типы являются особенными в том смысле, что они являются не ссылкой на выделенный объект, а скорее значениями, которые дублируются и копируются. Это всего лишь решение о реализации, принятое при определении языка, которое должно было обрабатывать значения вне объектной системы и быть настолько эффективным и «встроенным»; мода, насколько это возможно.
Это не совсем объясняет, почему они разрешены как инициализаторы в типе, но воспринимается как #define
, и тогда это будет иметь смысл как часть типа, а не как часть объект.