Также проверьте алгоритм Бойера-Мура для сопоставления с одной строкой.
Вам необходимо определить статические переменные в единице перевода, если они не относятся к целочисленным типам.
В заголовке:
private:
static const char *SOMETHING;
static const int MyInt = 8; // would be ok
В файле .cpp:
const char *YourClass::SOMETHING = "something";
Стандарт C ++, 9.4.2 / 4:
Если статический член данных имеет значение const целочисленный или константный тип перечисления, его объявление в классе определение может указывать постоянный инициализатор, который должен быть интегральное постоянное выражение. В этом случае член может появиться в интегральные постоянные выражения внутри его масштабы. Член по-прежнему должен быть определен в области пространства имен, если он используется в программе и пространстве имен определение области не должно содержать инициализатор.
class A{
public:
static const char* SOMETHING() { return "something"; }
};
Я делаю это все время - особенно для дорогих параметров const по умолчанию.
class A{
static
const expensive_to_construct&
default_expensive_to_construct(){
static const expensive_to_construct xp2c(whatever is needed);
return xp2c;
}
};
Ошибка заключается в том, что вы не можете инициализировать static const char *
внутри класса. Здесь вы можете инициализировать только целочисленные переменные.
Вам нужно объявить переменную-член в классе, а затем инициализировать ее вне класса:
// файл заголовка
class Foo {
static const char *SOMETHING;
// rest of class
};
// файл cpp
const char *Foo::SOMETHING = "sommething";
Если это кажется раздражает, подумайте об этом, потому что инициализация может появиться только в одной единице перевода. Если бы он был в определении класса, он обычно включался бы в несколько файлов. Постоянные целые числа - это особый случай (что означает, что сообщение об ошибке, возможно, не так ясно, как могло бы быть), и компиляторы могут эффективно заменить использование переменной целочисленным значением.
Напротив, char *
переменная указывает на реальный объект в памяти, который должен действительно существовать, и это ' s определение (включая инициализацию), которое делает объект существующим. «Правило одного определения» означает, что вы не хотите помещать его в заголовок, потому что тогда все единицы перевода, включая этот заголовок, будут содержать определение. Их нельзя было связать вместе, даже если строка содержит одни и те же символы в обоих, потому что в соответствии с текущими правилами C ++ вы определили два разных объекта с одним и тем же именем, и это незаконно. Тот факт, что в них есть одни и те же символы, не делает это законным.
Чтобы ответить на вопрос OP о том, почему это разрешено только с целыми типами.
Когда объект используется как lvalue (то есть как нечто, имеющее адрес в хранилище), он должен удовлетворять «правилу одного определения» (ODR), т.е. он должен быть определен в одной и только одной единице перевода. Компилятор не может и не будет решать, в какой единице трансляции определять этот объект. Это ваша ответственность. Определяя этот объект где-то, вы не просто определяете его, вы фактически сообщаете компилятору, что хотите определить его здесь , в этой конкретной единице трансляции.
Между тем в языке C ++ интегральные константы имеют особый статус. Они могут составлять интегральные постоянные выражения (ICE). В ICE интегральные константы используются как обычные значения , а не как объекты (т.е. не имеет значения, имеет ли такое целое значение адрес в хранилище или нет). Фактически, ICE оцениваются во время компиляции. Чтобы облегчить такое использование интегральных констант, их значения должны быть видны глобально. Да и самой константе действительно не нужно реальное место в хранилище. Из-за этого интегральные константы получили особую обработку: было разрешено включать их инициализаторы в файл заголовка, а требование предоставить определение было ослаблено (сначала де-факто, затем де-юре).
Другие типы констант не имеют таких свойств. . Другие типы констант практически всегда используются в качестве lvalue (или, по крайней мере, не могут участвовать в ICE или чем-то подобном ICE), что означает, что они требуют определения.
Есть уловка, которую вы можете использовать с шаблонами для предоставления 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";
Если вы используете 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. Не ожидайте, что это будет портативным.
Инициализатор константы разрешен стандартом C ++ только для целочисленных или перечислимых типов. Для получения подробной информации см. 9.4.2 / 4:
Если статический член данных имеет тип константного целого или константного перечисления, его объявление в классе определение может указывать инициализатор константы, который должен быть выражением интегральной константы (5.19). В этом В этом случае член может появляться в целочисленных постоянных выражениях. Член по-прежнему должен быть определен в имени - область пространства имен, если она используется в программе и определение области пространства имен не должно содержать инициализатор.
И 9.4.2 / 7:
Статические элементы данных инициализируются и уничтожаются точно так же, как нелокальные объекты (3.6.2 , 3.6.3).
Итак, вы должны написать где-нибудь в файле cpp:
const char* SomeClass::SOMETHING = "sommething";
Чтобы ответить на вопрос почему , интегральные типы являются особенными в том смысле, что они не являются ссылкой на выделенный объект, а скорее значениями, которые дублируются и копируются. Это просто решение реализации, принятое при определении языка, которое должно было обрабатывать значения вне объектной системы и максимально эффективно и «встроенным» способом.
Это не совсем объясняет, почему они разрешены в качестве инициализаторов в тип, но воспринимайте его как #define
, и тогда он будет иметь смысл как часть типа, а не как часть объекта.