Karthik
Karthik

Reputation: 143

Const correctness for extern const pointers

I'm trying to define constant strings that I can consume in a cpp file such that neither the data, nor the pointer can be modified. The linker complains the following:

main.obj : error LNK2001: unresolved external symbol "char const * const g_someString"

Sample code:

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

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

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

This doesn't happen when the const char * const is replaced with const char * though. Is the compiler (MSVC 2015) optimizing out the g_someString definition in constants.cpp?

Upvotes: 1

Views: 1298

Answers (1)

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145279

Before

const char * const g_someString = "Hello";

you need to declare it as extern (e.g. by including the header), because const namespace level variables have internal linkage by default.


That said, you can just define the strings in the header. The separate compilation buys you the ability to modify strings without incurring a rebuild of lots of files, but apart from that, IMHO it's premature optimization.


To make string definitions in a header formally safe for inline functions, if that's desired, you need the strings (or least the pointers) to have extern linkage. One way to do that is to leverage a special exemption for templates in the One Definition Rule. E.g. like this:

// 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>;

Then usage like

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

Here the Strings::some_string pointer will be the same in all translation units.

An alternative is to define the strings within an inline function. Then you can use e.g. an enumeration to name them.

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.
}

with usage like

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

The inline function has extern linkage, and so it's the same in all translation units.

Upvotes: 4

Related Questions