59

Given is a class with a static member.

class BaseClass
{
public:
    static std::string bstring;
};

String has obviously to be default-initialized outside of the class.

std::string BaseClass::bstring {"."};

If I include the above line in the header along with the class, I get a symbol multiply defined error. It has to be defined in a separate cpp file, even with include guards or pragma once.

Isn't there a way to define it in the header?

Appleshell
  • 7,088
  • 6
  • 47
  • 96
  • Headers are not for initialization. They are for providing interface declarations. – Elazar Sep 17 '13 at 22:31
  • 5
    @Elazar If I have to provide multiple definition files just to initialize single members in multiple classes it's counterproductive, and if I provided a single definition file for multiple headers its counterintuitive. Initializing it in the header would be the most comfortable solution. – Appleshell Sep 17 '13 at 22:40
  • But that is the way it is. – mjs Nov 05 '13 at 11:06

7 Answers7

80

You can't define a static member variable more than once. If you put variable definitions into a header, it is going to be defined in each translation unit where the header is included. Since the include guards are only affecting the compilation of one translation unit, they won't help, either.

However, you can define static member functions! Now, at first sight that may not look as if it could help except, of course, that function can have local static variable and returning a reference to one of these behaves nearly like a static member variable:

static std::string& bstring() { static std::string rc{"."}; return rc; }

The local static variable will be initialized the first time this function is called. That is, the construction is delayed until the function is accessed the first time. Of course, if you use this function to initialize other global objects it may also make sure that the object is constructed in time. If you use multiple threads this may look like a potential data race but it isn't (unless you use C++03): the initialization of the function local static variable is thread-safe.

Patrick
  • 900
  • 7
  • 14
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 1
    That's not quite true since C++11. [class.static.data] §§3 allows giving the initializer (`brace-or-equal-initializer`) for `constexpr` (mandatory) and `const` (optional, only if literal type) members. `std::string` does not qualify though. – Deduplicator Nov 21 '14 at 19:55
  • 1
    Re "You can't define a static member variable more than once", well I can. Whether the OP can is quite another matter. ;-) The ODR has an exemption for statics in class templates, so that's one way to do it. – Cheers and hth. - Alf Nov 21 '14 at 21:02
  • 1
    Since this answer was posted we've got the inline object proposal, which I think is accepted for C++17. – Cheers and hth. - Alf Sep 28 '16 at 12:43
33

In C++17 you can use inline variables, which you can use even outside classes.

The inline specifier, when used in a decl-specifier-seq of a variable with static storage duration (static class member or namespace-scope variable), declares the variable to be an inline variable.

A static member variable (but not a namespace-scope variable) declared constexpr is implicitly an inline variable.⁽¹⁾

For example:

class Someclass {
public:
    inline static int someVar = 1;
};

Or,

namespace SomeNamespace {
    inline static int someVar = 1;
}

⁽¹⁾ https://en.cppreference.com/w/cpp/language/inline

Community
  • 1
  • 1
André Kugland
  • 855
  • 8
  • 20
  • 6
    Can you expand on what the consequences are of declaring a variable inline? Will it be initialized exactly one time (like all global initialized data)? I assume there is only one instance of the object, but how does this work across translation units (inline can sometimes prevent DLL replacement with function definitions when the implementation changes, IIRC). – Casey Kuball May 12 '21 at 20:09
15

Regarding

Isn't there a way to define [the static data member] in the header?

Yes there is.

template< class Dummy >
struct BaseClass_statics
{
    static std::string bstring;
};

template< class Dummy >
std::string BaseClass_statics<Dummy>::bstring = ".";

class BaseClass
    : public BaseClass_statics<void>
{};

An alternative is to use a function, as Dietmar suggested. Essentially that is a Meyers' singleton (google it).

Edit: Also, since this answer was posted we've got the inline object proposal, which I think is accepted for C++17.

Anyway, think twice about the design here. Globals variables are Evil™. This is essentially a global.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Can this be extended to define the contents of the bstring as a template argument somehow? i.e. something like: `class BaseClass : public BaseClass_statics` – Rick Deckard Apr 02 '15 at 16:36
  • 1
    @RickDeckard: I don't see any way now. Checking with g++ 4.9.2, it needs either a `constexpr` array of `char`, or an array with external linkage. I think the former with internal linkage would/could mean essentially different values for the template parameter in different translation units, and the latter clashes with the One Definition Rule. And defining a function to produce the value one could just as well define a function to produce the `std::string` value, i.e. the Meyers' singleton approach. Yet I remember once hacking together a macro based scheme for this. But not sure how general. – Cheers and hth. - Alf Apr 02 '15 at 22:29
  • thanks! if you can dig up that macro I'll be very grateful. Everything I've come up with requires the DerivedClass to do something (macro/template/etc) in the header AND the cpp file. This proposal looks interesting http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4121.pdf, but unfortunately I need something that works with any C++11 compiler today. – Rick Deckard Apr 03 '15 at 03:49
  • Anonymous downvoter, please explain your downvote. It's wrong and wrongheaded. – Cheers and hth. - Alf Feb 23 '17 at 19:19
  • Global variables are normally pure evil, but global constants are OK. – Patricio Rossi Sep 12 '17 at 19:04
  • Cheers for this. I'm stuck at C++11 so the C++14/17 methods are no good, but this lets me autogenerate header files with classes with static members that are self contained (instead of needing something in the C++ source file to complete it). – Russ Schultz Apr 26 '23 at 03:27
5

To keep the definition of a static value with the declaration in C++11 a nested static structure can be used. In this case the static member is a structure and has to be defined in a .cpp file, but the values are in the header.

class BaseClass
{
public:
  static struct _Static {
     std::string bstring {"."};
  } global;
};

Instead of initializing individual members the whole static structure is initialized:

BaseClass::_Static BaseClass::global;

The values are accessed with

BaseClass::global.bstring;

Note that this solution still suffers from the problem of the order of initialization of the static variables. When a static value is used to initialize another static variable, the first may not be initialized, yet.

// file.h
class File {
public:
  static struct _Extensions {
    const std::string h{ ".h" };
    const std::string hpp{ ".hpp" };
    const std::string c{ ".c" };
    const std::string cpp{ ".cpp" };
  } extension;
};

// file.cpp
File::_Extensions File::extension;

// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };

In this case the static variable headers will contain either { "" } or { ".h", ".hpp" }, depending on the order of initialization created by the linker.

Marko Mahnič
  • 715
  • 6
  • 11
3

§3.2.6 and the following paragraphs from the current c++ 17 draft (n4296) define the rules when more than one definition can be present in different translation units:

There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then [...]

Obviously definitions of static data members of class type are not considered to appear in multiple translations units. Thus, according to the standard, it is not allowed.

The suggested answers from Cheers and hth. - Alf and Dietmar are more kind of a "hack", exploiting that definitions of

static data member of a class template (14.5.1.3)

and

inline function with external linkage (7.1.2)

are allowed in multiple TU ( FYI: static functions defined inside a class definition have external linkage and are implicitly defined as inline ) .

clickMe
  • 993
  • 11
  • 33
2

No, it can't be done in a header - at least not if the header is included more than once in your source-files, which appears to be the case, or you wouldn't get an error like that. Just stick it in one of the .cpp files and be done with it.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • But isn't that what the include guards or pragma once do, ensure that it is only included once in the compilation process? – Appleshell Sep 17 '13 at 22:36
  • 4
    Include guards will prevent it from being included more than once in a `.cpp` (or, technically "a translation unit", but unless you do strange stuff, a source file, after preprocessing, is a translation unit). – Mats Petersson Sep 17 '13 at 22:45
  • Yeah except that it's a pain in the ass to create a cpp file for an "interface" class that would otherwise only require a single header which doesn't even need to be added to a project since it doesn't require compilation. An extra cpp does. – Virus721 Apr 02 '15 at 09:44
  • Well, if that's ALL there is in the file, then you COULD add it to another, already existing file, perhaps. – Mats Petersson Apr 02 '15 at 21:03
1

UPDATE: My answer below explains why this cannot be done in the way suggested by the question. There are at least two answers circumventing this; they may or may not solve the problem.


The bstring static member has to be linked to a specific memory address. For this to happen, it has to appear in a single object file, therefore it has to appear in a single cpp file. Unless you're playing with #ifdef's to make sure this happens, what you want cannot be done in the header file, as your header file may be included by more than one cpp files.

nickie
  • 5,608
  • 2
  • 23
  • 37
  • Re "therefore", there are two counter examples (each as its own answer) here. – Cheers and hth. - Alf Apr 03 '15 at 08:56
  • 1
    @Cheersandhth.-Alf Technically, I'm not sure if any of the two is a counter example. In the case of your answer, you moved the static member to a class template which is inherited; I'm skeptical whether this is semantically equivalent. Anyway, I updated my answer, which was written a few minutes after the question and intended to explain why this cannot be done as suggested. Cheers... – nickie Apr 04 '15 at 21:37
  • Well, doubt is a good thing, as a certain Thomas no doubt would agree. But anyway, note that "For this to happen, it has to appear in a single object file" is disproved by both those answers. C++ supports the mechanism of `inline` data, it's just that there's no syntax to express it directly (which is a shame). – Cheers and hth. - Alf Apr 04 '15 at 22:03