C++17 inline variable vs inline static variable - c++

I am a bit confused by the inline variable introduced by C++17. What are the differences between inline variable and inline static variable? Also will this be affected by scope?
inline T var_no_scope;
inline static T static_var_no_scope;
namespace scope {
inline T var_scope;
inline static T static_var_scope;
}
Any explanation will be appreciated!

At namespace scope:
inline static seems to be equivalent to just static.
inline on variables only has effect when you define the same variable in several translation units. Since static restricts the variable to a single TU, you can't have more than one definition.
At class scope:
inline can only appear on static variables.
It has its normal effect, allowing you to initialize the variable directly in the header. Either:
struct A
{
inline static int a = 42;
};
Or:
struct A
{
static int a;
};
inline int A::a = 42;
At function scope:
inline is not allowed.

For me it becomes more interesting when it is a data members. In C++17 you can declare your static data members as inline. The advantage is that you don't have to allocate space for them in a source file. For example:
class A
{
// Omitted for brevity
static inline int b = 0;
};
So int A::b; can be removed from the source file.

inline is applicable to variables only with static storage duration.
All of the variables in your example have namespace scope, giving them static storage duration. Declaring them inline has no net effect if they are static.
A variable inside a class, struct or union only has static storage duration if it is declared static. Those varibles must be static if they are to be inline.

Great answer about inline variables and why we want to use them can be found here. In short inline variable allows to have multiple definition of a variable (even across multiple files), that will result in one variable in memory. This allow constexpr global variables in header files.
header.h
namespace constants
{
inline constexpr double P = 1.11;
}
Behavior is undefined when definitions have different values, but this shouldn't be a problem when multiplication of definitions occur only due to header file.
Others have pointed to a nice application in classes:
template<typename T>
struct C
{
static inline constexpr int c = 10;
};
And then you can reference this variable anywhere, e.g. with:
C<int>::c;

The inline static variable can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition:
struct X
{
inline static int n = 1;
};
Inline variables eliminate the main obstacle to packaging C++ code as header-only libraries.
If you need to declare global variables that are shared between compilation units, declare them as inline variables in the header file.
Also will this be affected by scope?
Any of the following names declared at namespace scope have external linkage and also names declared without a namespace for external linkage including in multiple source files must be inline.
See this Example.
This link has valuable information about inline variables.

definition from c++ reference: citation: https://en.cppreference.com/w/cpp/language/inline
inline variable is defined as inline static keyword. Hence
inline static vartype yourvariablename // is a inline variable.
Interesting note about inline "A function defined entirely inside a class/struct/union definition, whether it's a member function or a non-member friend function, is implicitly an inline function if it is attached to the global module"(C++ 20)
Ok from personal experience here's why inline static is a really really big thing:
If all you need todo is initialize a static variable in a C++ file then you can remove the C++ file and use inline static initialization(but your code will require C++ 17 and up)
Initializing static variables in pre C++ 17 classname templates is just overly verbose. You would have to do something like:
template
class classname
{
static yourtype yourstaticvariable = yourvalue; // and you'll have to initialize it
};
template yourtype classname::yourstaticvariable = yourvalue;
OR with inline static you can just do:
template <class T>
class classname
{
static yourtype yourstaticvariable = yourvalue; // initialized
}
You can heavily exploit templates if you use inline static for initializing common things. Consider for instance the singleton where a inline static variable is perfect for tracking the instance and hence can be done in one file where before initializing the static variable would be painful. This becomes even more evident when you have multivariable templates.
You need less C++ include files and less lib projects if you exploit inline static for single C++ header. This can heavily trim down files in a big project.
But there some caveats with inline static:
You need C++ 17 and up
You have to pay attention to issues of putting your initialization in a .h file(which you probably want to do if you initialize with inline static). Although this isn't necessarily a inline static issue but the decision to potentially moving everything to a header could lead to possible issues like namespace collisions, reusing macros or other things if you not careful with your order of inclusion.
But overall it's a fantastic feature. When in doubt initialize with inline static and break the I need a c++ file to initialize a static variable pattern.

Related

inline static constexpr vs global inline constexpr

Suppose that I have a few inline constexpr variables (named as default_y and default_x) in a header file and I decided to move them to a class that they are completely related to and mark them static (cause it seems better in terms of design).
namespace Foo
{
inline constexpr std::streamsize default_size { 160 }; // not closely related to the class Bar
class Bar
{
public:
inline static constexpr std::uint32_t default_y { 20 }; // closely related to the class Bar
inline static constexpr std::uint32_t default_x { 20 }; // closely related to the class Bar
};
}
So the question is will this make a difference in terms of how and when they are initialized at the start of the program (and overall efficiency)? Will the inline keyword in this particular use case force the compiler to add some guard for these two variables and make accessing them slower? Or maybe because they're constexpr there is no need to do those stuff at runtime since their value can be retrieved from the read-only section of the executable and then be assigned to them at the start of the main thread?
I built the program once with inline static and once with static and there was no difference in the size of the binary compared to the previous solution so maybe the linker generated the exact same code (hopefully).
Placing static inline constexpr variables should not impact efficiency in any way. Due to constexpr they're const-initialized at compile time if it's possible. inline keyword here is helping you to initialize static variable inside the body of a class. You might find this material on the inline keyword interesting: https://pabloariasal.github.io/2019/02/28/cpp-inlining/

What's the difference between static constexpr and static inline variables in C++17?

With C++17 we get inline variables.
One of the uses for them is to define constant fields in classes.
So what's the difference between these two constant definitions:
class MyClass {
static constexpr int myFirstVar = 10;
static const inline int mySecondVar = 100;
};
Of course constexpr makes myFirstVar implicitly inline.
What's the better choice here, to use constexpr or inline?
Note: when you don't need constness, then inline makes it easier. With constexpr you don't have that choice.
You don't have to specify an initializer for mySecondVar at the point of declaration. Nor is the initializer required to be constexpr itself.
This means that if we attempt to define myFirstVar like this:
class MyClass {
static constexpr int myFirstVar;
};
int MyClass::myFirstVar = 1;
Or like this:
#include <cstdlib>
class MyClass {
static constexpr int myFirstVar = rand();
};
It's ill-formed either way. constexpr semantics demand it and for a good reason.
The inline specifier approach allows us to include a static variable definition in the header itself, without the initializer being constexpr; or if the initializer is fairly complex it doesn't have to be in the class definition itself.
So this is a perfectly valid header in C++17:
#include <cstdlib>
class MyClass {
static const int mySecondVar;
};
inline const int MyClass::mySecondVar = rand();
The standard promises us that all translation units that include the header will see the same value for the variable, even though we won't know what it is until run-time.
It's mostly a library writers tool. Assume your library is header only. Then in the olden days, what were your options if you needed a static constant defined like this?
Well, you could have an object file shipped with your library. It will be compiled from a translation unit that contains just the constant definition. Now the library isn't header-only.
Or you could rely on inline functions instead. The inline variable effect can be achieved with the following:
class MyClass {
static inline int mySecondVar();
};
inline int MyClass::mySecondVar() {
static const int value = rand();
return value;
}
But it's hidden behind a wall of syntax, and masks what is essentially a constant, with a function call operator.

constexpr static member vs variable

I stumbled upon some C++11 code that looks like this:
// some_file.h
namespace blah {
class X {
public:
constexpr const static std::initializer_list<uint64> SOME_LIST =
{1,2,3};
};
}
// some_file.cpp
#include "some_file.h"
namespace blah {
constexpr const std::initializer_list<uint64> X::SOME_LIST;
}
Which compiles fine. I assume the definition in the cpp file is used to avoid symbol duplication in each file that includes the header (please correct me if I'm wrong).
I then tried the following:
// my_file.h
namespace bleh {
constexpr const static char SOME_CONSTANT[] = "yay";
}
// my_file.cpp
#include "my_file.h"
namespace bleh {
// if I add this or any other variation, compilation breaks!
//constexpr const static char SOME_CONSTANT[];
}
The code above does not work if I add an explicit definition in the .cpp file. So I am wondering: is there symbol duplication in the second case? If so, is there a way to define the variable without an enclosing class?
The static keywords mean two different things here:
When you declare a variable or function at file scope (global and/or namespace scope), the static keyword specifies that the variable or function has internal linkage. When you declare a variable, the variable has static duration and the compiler initializes it to 0 unless you specify another value.
When you declare a data member in a class declaration, the static keyword specifies that one copy of the member is shared by all instances of the class. A static data member must be defined at file scope. An integral data member that you declare as const static can have an initializer.
C++ needs you to define static class members somewhere, because the class symbol is global (and so is your member). This can't be done in the header because of multiple definitions.
In the second case every compilation unit uses its own variable and there is no global symbol.

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.

C++ static variable

I am trying to design header only library, which unfortunately needs to have global static variable (either in class or in namespace).
Is there any way or preferred solution to have global static variable while maintaining header only design?
The code is here
There are a couple of options. The first thing that came to my mind was that C++ allows static data members of class templates to be defined in more than one translation unit:
template<class T>
struct dummy {
static int my_global;
};
template<class T>
int dummy<T>::my_global;
inline int& my_global() {return dummy<void>::my_global;}
The linker will merge multiple definitions into one. But inline alone is also able to help here and this solution is much simpler:
inline int& my_global() {
static int g = 24;
return g;
}
You can put this inline function into a header file and include it into many translation units. C++ guarantees that the reference returned by this inline function will always refer to the same object. Make sure that the function has external linkage.