2015-11-06 84 views
1

我想定义可以在cpp文件中使用的常量字符串,这样数据和指针都不能被修改。链接器抱怨如下:外部const指针的const正确性

main.obj:错误LNK2001:无法解析的外部符号 “字符常量* 常量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)是否优化了constants.cpp中的g_someString定义?

+0

在_constants.cpp_ – Karthik

+0

中的定义确保'constants.cpp'包含'constants.h'(并且没有循环依赖关系) –

+0

'void main'的定义之前,链接器错误消失了_extern_声明在C++中是非法的,即使你的编译器提供了一个不合规的扩展名,编写依赖于这些扩展名的代码也是不好的 –

回答

4

之前

const char * const g_someString = "Hello"; 

你需要将其声明为extern(例如通过包括头部),因为const命名空间层次变量的缺省内部链接。


也就是说,你可以在标题中定义字符串。单独的编译让您可以修改字符串而不会重建大量文件,但除此之外,恕我直言,这是过早的优化。


为了使字符串定义在头正式安全的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; } 

inline功能有extern联动,因此它在所有的翻译单位相同。

+0

单独编译也可以帮助编译器执行字符串池。 –

+0

在头文件中定义字符串的一个陷阱是,如果一个'inline'函数使用这样的字符串,它会导致未定义的行为。 (并不是说你不应该在头文件中定义字符串,只是必须知道这个陷阱) –

+0

@ M.M:谢谢,我没有想到(尽管我一度意识到这一点)。我只想过是否提及如何在外部链接的标题中做到这一点,但决定只会使答案复杂化。现在我不太确定,嗯。无论如何,要定义标题中的字符串和外部链接,可以使用旧的模板常量技巧。 –