Как объявить статический const char* в вашем заголовочном файле?

StackOverflow https://stackoverflow.com/questions/1639154

  •  08-07-2019
  •  | 
  •  

Вопрос

Я бы хотел определить постоянный символ * в моем заголовочном файле для использования моего .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 , и тогда это будет иметь смысл как часть типа, а не как часть объект.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top