14

Most of the time I see constant C-strings defined as:

static char const* MY_CONSTANT = "Hello World";

However, the pointer itself is not const. Wouldn't it be more appropriate to do it like below?

static char const* const MY_CONSTANT = "Hello World";

There are 2 goals with constant globals like this, I think:

  1. Do not allow modification of the string
  2. Do not allow the variable to point to anything else

I simply assumed these 2 goals were needed when defining constant strings.

Another interesting thing is that I am allowed to do this:

int main()
{
    auto MY_CONSTANT = "";
    MY_CONSTANT = "Another String";
}

This tells me that auto deduces the string as char const* and not char const* const.

So I have two main questions:

  1. What is the most appropriate way to define constant c-style strings (I suppose constant pointers-to-something, is the more general question?). Why do you choose one or the other?
  2. Concerning my example with auto, it makes sense why it chooses char const* (because it's the array of data that's constant, not the pointer itself). Could I make auto deduce to char const* const or can I change the code to make it result in such a type?
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • Don't you mean `const char* const`? – Adam Sep 30 '14 at 01:16
  • 4
    @Adam Both forms are valid C++. Yours does the same as mine. – void.pointer Sep 30 '14 at 01:16
  • 1
    the default that `auto` detects is the minimal restriction, which means that the fact that the variable cannot be reassigned is just your choice. you can do `auto const` I suppose, if you want, but its best not to impose it on everybody. – v.oddou Sep 30 '14 at 01:17
  • 4
    A string literal can be binded to a `const char *`; whether you wanna the string variable to be constant is up to you. You are asking the same question as `should I use const int i=0 or int i=0` – SwiftMango Sep 30 '14 at 01:18
  • I think OP alludes to the fact that there's a lot of code out there that forgets to use top-level const when it was intended that the pointer would not be changed. If you have to program on an embedded environment where this makes a difference to how much static data are you have available, you learn pretty quickly to put the extra `const` in! – M.M Sep 30 '14 at 01:32
  • 2
    Another variety you didn't mention is `char const s[] = "Hello, World!";` . (In C++ the keyword `static` is redundant because namespace-scope `const`s default to being static, although it is practical to use it anyway in case someone ports your header to C and doesn't realize this). – M.M Sep 30 '14 at 01:33
  • Neither `static char const* MY_CONSTANT = "Hello World";` nor `static char const* const MY_CONSTANT = "Hello World";` is more correct that the other. They mean different things. Use whichever one is more appropriate for your current needs. – Keith Thompson Sep 30 '14 at 14:55

4 Answers4

10

Well if it a truly a constant then constexpr would be the C++11 way to do this:

constexpr char const* MY_CONSTANT = "Hello World";

The first example:

static char const* MY_CONSTANT = "Hello World";

just says I have a pointer to a char const that has static storage duration, which if it is outside of a function would make it a global.

If we required the pointer to also be const then we need the second form your introduced. It all depends on whether the pointer is indeed supposed to const or not. In most cases, the reason your see code without the top-level const is because they just forgot to put it in not because they did not mean the pointer to also be const.

Where static makes a difference for example if whether you want a const member to be per-instance or per-class:

class A
{
        char const* const const_per_instance = "Other Const String";
    public:
        constexpr static char const* const const_per_class = "Hello World" ;
};

If we require the const to be per-class then we need to use static otherwise not. The example changes slightly if you are not allowed to use constexpr:

class A
{
        char const* const const_per_instance = "Other Const String";
    public:
        static char const* const const_per_class  ;
};

char const* const A::const_per_class = "Hello World" ;

but the essence is the same just the syntax is different.

For your second question as Gotw #92 says auto drops top level const, one example given is as follows:

const int   ci  = val;  
auto        g   = ci;

and it says:

The type of g is int.

Remember, just because ci is const (read-only) doesn’t have any bearing on whether we want g to be const. It’s a separate variable. If we wanted g to be const, we would have said const auto as we did in case c above

the example that is being referred to is as follows:

int         val = 0;  
//..
const auto  c   = val;
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
2
constexpr auto& MY_CONSTANT = "Hello World";
  • MY_CONSTANT has type const char (&)[12]
  • No decay (the array bound isn't lost)
  • Everything is constant - the array itself and the reference (by definition)
  • Everything is constexpr (can be used at compile time) - the array itself and the reference
  • MY_CONSTANT has internal linkage due to constexpr and can be used in headers
user2665887
  • 863
  • 5
  • 19
0

It's a rare case where it happens that you do overwrite your pointer which points to a const value, therefore most developers omit the second const but semantically it would be indeed correct this way:

static char const* const MY_CONSTANT = "Hello World";

or in this form:

static const char* const MY_CONSTANT = "Hello World";

constexpr for the declaration is just needed if it is part of another constexpr function like this:

static constexpr const char* const MY_CONSTANT = "Hello World";
static constexpr const char* Foo()
{
    // ...
    return MY_CONSTANT;
}
HelloWorld
  • 2,392
  • 3
  • 31
  • 68
0

Well done for noticing the const on the pointer part! Many people don't realise that bit.

To prevent the string literal from being duplicated per translation unit (or make it easier for the string-pooling part of the optimizer), I suggest putting the actual data in a source file somewhere. This will also save some recompiling if you change the text.

header:

extern const char *const mystring;

source:

extern const char *const mystring = "hello";

Alternatively

header:

extern const std::string mystring;

source:

extern std::string mystring = "hello";

Neil Kirk
  • 21,327
  • 9
  • 53
  • 91