2015-11-06 4 views
1

Я пытаюсь определить константные строки, которые я могу использовать в файле cpp, так что ни данные, ни указатель не могут быть изменены. Компоновщик жалуется следующее:Корректность констант для указателей const extern

main.obj: ошибка LNK2001: неразрешенный внешний символ "символ сопз * Const g_someString"

Пример кода:

//constants.h 
extern const char * const g_someString; 

//constants.cpp 
const char * const g_someString = "Hello"; 

// main.cpp 
int main() 
{ 
    strcmp(g_someString, "Hello"); 
} 

Это не происходят, когда const char * const заменяется на const char *. Является ли компилятор (MSVC 2015) оптимизацией определения g_someString в файле constants.cpp?

+0

Ошибка компоновщика исчезает с объявлением _extern_ перед определением в _constants.cpp_ – Karthik

+0

убедитесь, что 'constants.cpp' включает' constants.h' (и у вас нет круговых зависимостей) –

+0

'void main' is незаконно в C++, даже если ваш компилятор предлагает неконфигурирующее расширение, нехорошо писать код, который полагается на такие расширения. –

ответ

4

Перед

const char * const g_someString = "Hello"; 

вам нужно объявить его как extern (например, путем включения в заголовок), поскольку переменные уровня const пространства имен имеют внутреннюю связь по умолчанию.

Сказанное: вы можете просто определить строки в заголовке. Отдельная компиляция покупает вам возможность изменять строки без возникновения перебора большого количества файлов, но кроме того, IMHO - это преждевременная оптимизация.


Для определения строк в заголовке формально безопасного для inline функций, если это необходимо, вам нужны строки (или хотя бы указатели), чтобы иметь extern связи. Один из способов сделать это - использовать специальное исключение для шаблонов в правиле одного определения. Например. как это:

// Perhaps best generated by some code generation facility: 
template< class Dummy > 
struct Strings_ 
{ 
    static char const* const some_string; 
    static char const* const some_other_string; 
}; 

template< class Dummy > 
char const* const Strings_<Dummy>::some_string = "Blah!"; 

template< class Dummy > 
char const* const Strings_<Dummy>::some_string = "Oh, well."; 

using Strings = Strings_<void>; 

Тогда использование как

inline void foo() { cout << Strings::some_string << endl; } 

Здесь Strings::some_string указатель будет одинаковым во всех единицах трансляции.

Альтернативой является определение строк в пределах функции inline. Затем вы можете использовать, например. перечисление, чтобы назвать их.

enum String_id { some_string, some_other_string }; 

inline 
auto strings(String_id const id) 
    -> char const* 
{ 
    switch(id) 
    { 
    case some_string:   return "Blah!"; 
    case some_other_string: return "Oh, well."; 
    } 
    assert(false); // Should never get here. 
} 

с использованием как

inline void foo() { cout << strings(some_string) << endl; } 

inlineextern функция имеет связи, и таким образом это то же самое во всех единицах трансляции.

+0

Отдельная компиляция также может помочь компилятору выполнить пул строк. –

+0

Ловушка с определяющими строками в заголовке состоит в том, что если функция 'inline' использует такую ​​строку, она вызывает неопределенное поведение. (Не сказать, что вы не должны определять строки в заголовке, просто нужно знать об этой ловушке) –

+0

@ M.M: Спасибо, я не думал об этом (хотя я знал об этом в одно время). Я только подумал, стоит ли упоминать, как это сделать в заголовке с внешней связью, но решил, что это просто усложнит ответ. Теперь я не уверен, хм. В любом случае, чтобы определить строки в заголовке и с внешней связью, можно просто использовать старый шаблонный трюк. –

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