Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I make in-class initialized `const const std::string` a static member

I have the following working code:

#include <string>
#include <iostream>

class A {
public:
  const std::string test = "42";
  //static const std::string test = "42"; // fails
};

int main(void){
  A a;
  std::cout << a.test << '\n';
}

Is there a good reason why it is not possible to make the test a static const ? I do understand prior to c++11 it was constrained by the standard. I thought that c++11 introduced in-class initializations to make it a little bit friendlier. I also not such semantic are available for integral type since quite some time.

Of course it works with the out-of class initialization in form of const std::string A::test = "42";

I guess that, if you can make it non-static, then the problem lies in one of the two. Initializing it out-of-class scope (normally consts are created during the instantiation of the object). But I do not think this is the problem if you are creating an object independant of any other members of the class. The second is having multiple definitions for the static member. E.g. if it were included in several .cpp files, landing into several object-files, and then the linker would have troubles when linking those object together (e.g. into one executable), as they would contain copies of the same symbol. To my understanding, this is exactly equal to the situation when ones provides the out-of-class right under the class declaration in the header, and then includes this common header in more than one place. As I recall, this leads to linker errors.

However, now the responsibility of handling this is moved onto user/programmer. If one wants to have a library with a static they need to provide a out-of-class definition, compile it into a separate object file, and then link all other object to this one, therefore having only one copy of the binary definition of the symbol.

I read the answers in Do we still need to separately define static members, even if they are initialised inside the class definition? and Why can't I initialize non-const static member or static array in class?.

I still would like to know:

  1. Is it only a standard thing, or there is deeper reasoning behind it?
  2. Can this be worked-around with the constexpr and user-defined literals mechanisms. Both clang and g++ say the variable cannot have non-literal type. Maybe I can make one. (Maybe for some reason its also a bad idea)
  3. Is it really such a big issue for linker to include only one copy of the symbol? Since it is static const all should be binary-exact immutable copies.

Plese also comment if I am missing or missunderstanding something.

like image 849
luk32 Avatar asked Jun 27 '13 19:06

luk32


People also ask

How do you declare a static string variable in C++?

cpp: string foo::fooString = "foo"; And also: In my particular case foo::fooString should represent a path variable, and I would like that for every object of class foo there were just one instance of foo::string, representing a const value that should never change.

Can static members be const?

A static data member can be of any type except for void or void qualified with const or volatile. You cannot declare a static data member as mutable.

What's the class of a constant string?

Static constant string (class member)


1 Answers

Your question sort of has two parts. What does the standard say? And why is it so?

For a static member of type const std::string, it is required to be defined outside the class specifier and have one definition in one of the translation units. This is part of the One Definition Rule, and is specified in clause 3 of the C++ standard.

But why?

The problem is that an object with static storage duration needs unique static storage in the final program image, so it needs to be linked from one particular translation unit. The class specifier doesn't have a home in one translation unit, it just defines the type (which is required to be identically defined in all translation units where it is used).

The reason a constant integral doesn't need storage, is that it is used by the compiler as a constant expression and inlined at point of use. It never makes it to the program image.

However a complex type, like a std::string, with static storage duration need storage, even if they are const. This is because they may need to be dynamically initialized (have their constructor called before the entry to main).

You could argue that the compiler should store information about objects with static storage duration in each translation unit where they are used, and then the linker should merge these definitions at link-time into one object in the program image. My guess for why this isn't done, is that it would require too much intelligence from the linker.

like image 134
Andrew Tomazos Avatar answered Sep 23 '22 02:09

Andrew Tomazos