Any potential pitfall of using inline static data member? - c++

C++17 introduced inline variable, and an inline static data member can be defined in the class definition with an initializer. It does not need an out-of-class definition. For example,
struct X {
inline static int n = 1;
};
Given this, I see no reason not to always use inline static data members, for the neat syntax. Any pitfall of doing this? Note that I don't mind slower compilation.

Not a pitfall, but here's one reason not to use an inline: if the initial value of the variable is not just a trivial constant, but something more complicated:
struct X {
inline static int n = and_then_more(figure_out_where_n_comes_from());
};
Now, the declaration of figure_out_where_n_comes_from() and and_then_more() must be pulled into the header file, now.
Also, whatever figure_out_where_n_comes_from() returns must also be declared. It could be some horribly overcomplicated class, which then gets passed to and_then_more(), as a parameter, to finally compute the initial value for n.
And everything that #includes the header file where X is declared must now include all the header files for all of these dependencies.
But without an inline, all you have is:
struct X {
static int n;
};
And you need to deal with all these dependencies only in one translation unit that instantiates X::x. Nothing else that #includes only X's header file cares about it.
In other words: information hiding. If it's necessary to reimplement where the initial value of n comes from, you get to recompile only one translation unit, instead of your entire source code.

Related

Avoid compiling definition of inline function multiple times

I have a non-template struct in a header file:
struct X {
constexpr X() : /* ... */ { /* ... */ }
constexpr void f() {
// ...
}
};
With functions of varying size. This is used in a lot of different translation units, and each function appears in multiple object files for them to be discarded in the final executable.
What I want is for the definitions to be in a single object file, and the other translation units can either inline the function or use an external definition (something like the extern inline semantics from C). How can I do that?
It seems to work with templates and extern template:
namespace detail {
template<std::nullptr_t>
struct X_base {
constexpr X_base() // ...
constexpr void f() // ...
};
extern template struct X_base<nullptr>;
}
struct X : detail::X_base<nullptr> {
using X_base::X_base;
};
// X.cpp
#include <X.hpp>
template struct detail::X_base<nullptr>;
But are there any major downsides to this (longer symbol names, confusing to read, needs documentation, etc.), or are there any easier ways to do this?
C++ doesn’t have the notion of an inline function that must be emitted in one translation unit and which therefore certainly need not be emitted anywhere else. (It doesn’t have the notion of emitting object code at all, but the point is that there’s no syntax that says “I promise this definition is ODR-identical to the others except that it and only it bears this marker.” so that compilers could do that.)
However, the behavior you want is the obvious way of implementing C++20 modules: because the definition of an inline function in a module is known to be the only definition, it can and should be emitted once in case several importing translation units need an out-of-line copy of it. (Inlining is still possible because the definition is made available in a compiler-internal form as part of building the module.) Bear in mind that member functions defined in a class in a module are not automatically inline, although constexpr still implies it.
Another ugly workaround is to make non-inline wrappers to be used outside of constant evaluation, although this could get unwieldy if there were multiple levels of constexpr functions that might also be used at runtime.

How to initialize static members in the header

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?
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.
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
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.
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.
§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 ) .
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.
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.

Static Variable initialisation for Classes in C++, why include data type?

I've been learning C++, and I've come across static variable (I have prior knowledge from C89), and in the resource i'm using, they've declared a static variable in a class such as:
class nameHere
{
public:
static int totalNum;
}
int nameHere::totalNum = 0;
int main()
{}
For Example.
What I don't understand is that, since I've already declared that the static variable is an integer in the class definition, why do I need to also declare it as an integer outside of the class definition?
Would it not make sense to simply initialise it like so:
nameHere::totalNum = 0;
int main()
{}
Is there a particular reason or simply a convention of C++?
Thanks for all the help!
This would (probably) make the language even more difficult to parse (and it's already almost insanely difficult to parse anyway).
As it is, the datatype (int, long, my_class, whatever) tells the compiler that what it's seeing is the beginning of a declaration (which, in this case, is also a definition). Without that, the compiler would have a rather more difficult time sorting things out.
In the specific case of things at global scope, it wouldn't be that bad, because at global scope about all you can have is a series of declarations. At any other scope, however, things would be more difficult (and having one rule at global scope, and another elsewhere would be ugly indeed).
In C++11 you can simply initialize the variable inside the class:
class nameHere
{
public:
static const int totalNum = {0};
}
There is a difference between a definition and a declaration.
While the static variable in the class has been declared, it has not been defined. The One Definition Rule, explains declarations and definitions and states
In any translation unit, a template, type, function, or object can have no more than one definition. Some of these can have any number of declarations.
Therefore, the full type of the object must be used when declaring the variable.

Need for out of class definition of static variable?

My question: What exactly is the out of class definition of k doing under the hood to make sure it's address is available?
#include <iostream>
using namespace std;
class A {
public:
static const float k = 7.7;
};
//const float A::k; --> without this line compiler error
int main()
{
cout << &A::k;
}
The class "definition" is actually only providing a "declaration" of A::k. Yeah, I know it's confusing, but the idea is to allow the class definition to be in a .h (included from multiple .cpp sources) without creating ambiguities: one, and only one, of those .cpp sources, must provide the actual definition to match A::k's declaration (the latter being part of class A's definition).
Other answers have already given the how to fix it -- I'll go more into the why.
When you do this:
#include <iostream>
using namespace std;
class A {
public:
static const float k = 7.7;
};
int main()
{
cout << A::k;
}
The compiler is probably in reality generating this:
#include <iostream>
using namespace std;
class A {
public:
static const float k = 7.7;
};
int main()
{
cout << 7.7;
}
which means there will be no link-time dependency between this translation unit and A::f -- the compiled code doesn't reference A::f at all!
However, when you use &A::f, you force the compiler to generate an address for A::f. Therefore that translation unit does have a dependence on A::f. Since you have not defined it, you get a linker error, because the linker cannot find an address for it. For the address to exist, A::f must be defined in one and only one translation unit. To choose the translation unit in which it should reside, you need to define it there.
You also have an invalid code issue with your class above though -- only static const integral members may be initialized with the syntax you've used (putting k = 7.7 in the class body) -- float is not an integral type.
It sounds like you're wondering why the variable needs to be defined even though you aren't accessing it.
To print its address, it needs to have an address. To have an address, it must exist. To exist, it needs to have a definition and the linker needs to assign it a place in global variable space. So, there really isn't a middle ground.
"Under the hood," the definition tells the linker what the initializer for the global is. (In this case, the initializer is in the class block. But that's nonstandard. The official way is to write const float A::k = 7.7;) Without knowing that, it can't produce the executable file.
Also, unless the compiler performs impossibly detailed analysis, it can't really tell that operator << doesn't somehow pass that pointer to some other function or OS service that will access the value of k.
If you could define like static const float k = 7.7; as you wish, you will end up in multiple definitions (since static members will be defined only once), wherever you are including it.
To avoid that the definition is made reside separately in a cpp file.
From C++ standard docs sec 9.4.1,
A static data member is not part of the subobjects of a class. There is only one copy of a static data member shared
by all the objects of the class.
Also 9.4.2 states that,
The declaration of a static data member in its class definition is not a definition and may be of an incomplete type
other than cv-qualified void. The definition for a static data member shall appear in a namespace scope enclosing the
member’s class definition.
Hope it helps..
The one which is inside the class is the declaration of the variable k. You need to define it in exactly one translation unit in order to link your program correctly. Hence that statement is required.
A static variable can be deemed as data shared by all the objects of the class and hence only one copy of this variable should be created. With this said, with whom should lie the responsibility of allocationg memory for this member? Obviously it can't be object's responsibility as there could be multiple objects which would raise another challenge as to which object should allocate memory for this member.
So typically compliler expects out of class, explicit definition of this member and hence the line:
const float A::k;
This ensures that the static member variable is accessible to all the objects of the class. The memory for this variable is allocated on globally accessible memory.

Rationale behind static const (non-integral) member initialization syntax?

I know how to initialize a static member that's not an integer, but I'm wondering, what is the rationale behind the syntax for this? I'd like to be able to just put the value in the class, like you can with an integer member, a la:
class A {
static const int i = 3;
};
I realise this could mean more rebuilding if I change the value since it's a change in the header - but in some cases that's pretty unlikely - and just as bad as changing a #define in the header anyway.
It doesn't seem like something that would be terribly hard for the compiler to understand. Are there technical reasons why it works the way it does? Or is it just a case of the compiler enforcing the good practice of separating the implementation from the definition?
Because that is the class declaration. You don't have any object yet.
You need to actually define the value somewhere --- somewhere specific.
Since it is static it's actually taking up space somewhere. But, since the .H file which has that declaration can be #included in many source files, which one defines holds the actual space it is using? Having the compiler automatically define the space in every object file and having the linker sort it out would be a violation of the "One Definition Rule".
A static class member has linkage, so it needs to be in a source file. Just because you declare it const doesn't mean it really can't change (please look up volatile for example).
This might help you:
class A {
enum { i = 3 }; // use an enum to set a constant value in the class declaration
void f() { int k = int(i); }
}