Static constexpr data member initialized off-class, and a possible workaround? - c++

From elsewhere I know that it's not possible in C++17 to declare a static constexpr data member without its immediate initialization (although yet elsewhere they use such example).
// --- in header ---
struct Data{
LPCTSTR str;
int i;
};
class C{
public:
static constexpr Data MyData; // VS complains: this requires an in-class initializer
};
// --- in implementation file ---
constexpr Data C::MyData={
_T("Hi"), 12345
};
My code must, for the time being, compile also in older Visual Studios where the lack of constexpr concept can easily be #defined as
#if _MSC_VER<=1600 // pre-C++11 compiler?
#define constexpr const
#endif
and suddenly the code base would be compatible with virtually anything from VS6 to VS2019. Correct me if the off-class initialization is allowed by means of some hidden command-line switch in Visual Studio.
So I came up with a workaround about whose validity I'm not sure. Unlike data members, static constexpr methods don't have to have an in-class body. So I do:
// --- in header ---
struct Data{
LPCTSTR str;
int i;
};
class C{
public:
static constexpr Data GetMyData();
};
// --- in implementation file ---
constexpr Data C::GetMyData(){
constexpr Data D={
_T("Hi"), 12345
};
return D;
}
I can then benefit from constexpr, yet still compile (with the help of the above #define) under older Visual Studios:
constexpr LPCTSTR Greeting=C::GetMyData().str;
and also surprisingly take the address of the returned object at run-time:
const Data *p=&C::GetMyData();
const LPCTSTR greeting=p->str;
which is great and desired in my app! But makes me worry about the address of what I have received? Is it the address of the object compiled into the resulting executable (and at run-time residing in the protected memory) or an address of a global variable created really at run-time? I of course want the earlier to occur when compiling under newer Visual Studios, whereas I don't care about the situation under pre-C++11 versions.
Thanks in advance.

The key point is that constexpr does not mean "exists only at compile time", but instead "can also be used at compile time. So as long as you don't access the constexpr variable in a way that requires the value to be accessible at compile-time, it can be treated as if it was a const.
In fact, with the code you posted, the only place where you ever see the benefits of constexpr is inside of the implementation file. Anywhere else, it has not choice but to be "degrated" to a regular const since resolving its value during compilation is impossible.
I think most of your questions can be cleared up by deconstructing the code a bit into an equivalent example:
// MyClass.h
class MyClass {
// static constexpr Data MyData;
static const Data MyData;
};
// MyClass.cpp
namespace {
constexpr Data MyDataValue = ...;
}
const Data MyData::MyData = MyDataValue;
// If you use MyData inside of the implementation file, it gets the conxtepr value.
void MyClass:foo() {
// std::array<float, MyClass::Data.some_member> some_array;
std::array<float, MyDataValue.some_member> some_array; // no problem
}
// Some_other_file.cpp
int foo() {
// That's fine, MyClass::Data will be resolved either at runtime or during lto.
return MyClass::Data.some_member;
}
int bar() {
std::array<float, MyClass::Data.some_member> some_array; // ERROR! Can't be resolved at compile time
}
So what's the point of being able to declare a static constexpr data member without its immediate initialization?
As long as you define it later inside a header before it's actually used, then everything is fine, which can sometimes come in handy. However, in your case, since you want to define the variable inside an implementation file, you create this two-tiered system where the member is constexpr in the implementation file, and const anywhere else.
All this to say: If all your delayed constexpr definitions are in implementation files like in your posted code, then just make them const, and use locally a constexpr in an anonymous namespace inside of the implementation file if you need it there. No need for the macro at all.

Related

constexpr in class without static

I have a situation where I need to store a compile time constant in a header, and I do it in the class I'm using it in as I don't want to expose the constant to other files that include this header. (as well as the fact that it depends on another hidden struct)
Like this:
namespace ns {
class bla {
private:
struct internalStruct {
// ...
};
// I put it in the class as I don't want other files to be able to see this
constexpr const size_t compileConstant = sizeof(internalStruct) * 8;
};
}
The problem is I get a
Constexpr is not valid here
error. The solution is to add static, however I read that constexpr integral members should be inline.
What should I do?
Adding static should be perfectly fine for your use case. That's what the reference states too:
A constexpr specifier used in a function or static data member (since
C++17) declaration implies inline.
And since this is a compile-time constant, you might as well have it shared across all class instances rather than on a per-instance basis (which is usually how even const variables are used).

Linking Static Variable in Abstract Class [duplicate]

Say that I have a class that requires a few constants to function. Several member functions require use of these constants. Use of #define is frowned upon since it can cause collisions. The constants are hex patterns of 8 or 16 bits and are stored as uint8_t or uint16_t. These constants also don't change from instance to instance of the class, and therefore memory (albeit very little memory) can be saved by having only one copy of the constants.
Is there anything improper, or perhaps of better way of accomplishing the above instead of simply doing something like the following:
// mycode.h
// .......
class myclass {
private:
static const uint16_t kMyClassConstant_ = 0xBEEF;
// .......
};
Thanks in advance for the help.
Given your description of the situation, I'd say using static const members is a good approach. In C++11 you may want to change it into static constexpr to emphasize it's a compile-time constant, although nothing will effectively change as a result of that.
If you refer to myclass::kMyClassContant_ somewhere in the code in a way that is relevant under the one-definition-rule (odr), esp. in contexts that require a reference (including const-reference), the compiler will complain that there is no definition of the constant. Merely declaring and initializing it inside the class isn't sufficient in this case. This may force you to separate declaration and definition:
// mycode.h
class myclass {
private:
static const uint16_t kMyClassConstant_;
};
// mycode.cpp
const uint16_t myclass::kMyClassConstant_ = 0xBEEF;
To avoid the trouble of maintaining separate declarations and definitions, some people prefer declaring an inline constexpr function instead of an actual variable:
// mycode.h
class myclass {
private:
static constexpr uint16_t kMyClassConstant_()
{ return 0xBEEF; }
};
This is a correct work-around for many of the odr-related problems, and it does not cause any loss in performance. Whether it is really useful depends on how much of a burden it is to maintain separate declarations and definitions of an ordinary static constant. If you expect your constants to never change as your code evolves, using ordinary static constants with separate definitions is preferable. But if you modify the definitions of the constants frequently, having to re-compile the definition file and re-link it to all relevant parts of the project may make you consider the function-based solution above as a better alternative.
A final comment on the data type: Forcing it into 16 bits using std::uint16_t can be useful if you need to store lots of these values in compact form. Otherwise, the actual size may not really matter, in which case std::uint_fast16_t (which may be larger than 16 bits) may be better.
You could use type traits to implement this:
#include <type_traits>
class myclass {
private:
typedef std::integral_constant<uint16_t , 0xBEEF> kMyClassConstant;
// ...
};
used as myclass::kMyClassConstant::value.
This shows the purpose of implementing an integral constant and prevents you from accidentaly taking an address of the constant.
Since C++17, we have access to inline variables, which take care of the odr-related problems. Several options:
// mycode.h
class myclass {
static const inline uint16_t kMyClassConstant_ = 0xBEEF;
};
Or, if it can be marked constexpr (like in this case):
// mycode.h
class myclass {
static constexpr inline uint16_t kMyClassConstant_ = 0xBEEF;
};
Which can be simplified to:
// mycode.h
class myclass {
static constexpr uint16_t kMyClassConstant_ = 0xBEEF;
};
Because in C++17 constexpr implies inline for static data members.

Why can't a static constexpr member variable be passed to a function?

The following code produces an undefined reference to 'Test::color'.
#include <iostream>
struct Color{
int r,g,b;
};
void printColor(Color color) {
//printing color
}
class Test {
static constexpr Color color = {242,34,4};
public:
void print(){
printColor(color);
}
};
int main() {
Test test;
test.print();
return 0;
}
Why does this code produce the above error and what is the best way to avoid it, considering I want to use the latest version of the standard, C++17?
Should I define the static member variable, just like it was needed in earlier revisions of the standard (see the first answer here: Undefined reference to static constexpr char[]) or should I just create a new Color structure as can be seen below?
printColor(Color{color.r, color.g, color.b});
Edit:
I'm using CLion on Ubuntu 16.04, which as far as I could find out, uses g++ 5.4 for compiling. I have set it to use C++17 and still get the same error. The error is only present when color is passed to a function.
This is due to the fact that, before C++17, you had to specifically define the static variable outside the class:
class Test {
/* ... etc etc ... */
}
const constexpr Color Test::color;
The constexpr-ness of the static member does not let you 'waive' this explicit definition requirement.
With C++17, you no longer need to define static members explicitly. They are implicitly "inline" variables, which get auto-defined at some point, and just once per binary, without you having to take care of it. See here for the long proposal of this feature.
Note that the definition must only appear in a single translation unit (so probably not in a header with class Test that gets included a lot).
The problem was neither with the code itself, nor with the standard being used. CLion's default compiler does not fully support C++17, so that's why it showed a strange behavior that it could compile static constexpr member variables, but only as long as they were not passed to functions.
After updating to the most recent compiler version, I was able to run the code successfully without any changes.
Thank you for all your contribution.

When is definition of class' static data member (un/-)necesary

I have a big project and work on refactoring it. Major task is rewrite of logger. New logger is (as far as I can tell) API-compatible with old one, so I believed that after changing header include directory, recompile and relink everything should work. But no. I get multiple errors of the kind undefined reference to <static_data_member>. I can't paste actual code, but it looks for example like this:
// Foo.h
class Foo {
static const int bar = 0;
int baz; // assigned in c-tor
void updateBaz() { baz = bar; }
// ....
}
static const int bar is NOT defined in Foo.cpp. It is sometimes printed by log macros. And it used to work (with old logger), now I have to define it. What change could have caused it?
Another example that that occurs with variables declared by boost:
(...)/blog_adaptor.h:50: error: undefined reference to bbost::serialization::version<CA::CReyzinSignature>::value'
So: when are definitions to static members required and when can they be omitted?
Unless the variables are declared inline (a C++17 feature), definitions of static member variables are not optional, as far as the C++ standard is concerned. Failure to provide a definition is undefined behavior.
Compilers and linkers may vary on exactly what will make them check to see if definitions exist, but that is the nature of undefined behavior.
As Nicol Bolas answered, the code in my project had undefined behavior because static data members were initialized but nowhere defined. To summarize and extend:
Static data member doesn't need to be defined when:
It is not used or is used only in discarded branches (non-instantiated templates and discarded branches of constexpr-if)
in C++17 if member is inline
also clang-tidy says that "out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated", so probably static constexpr also doesn't need it
Further, following code shows why my bad project was not triggering linker error before. I don't know whether it is "not odr-use" or "Undefined Behavior that doesn't hurt you yet":
#include <boost/serialization/version.hpp>
class Klass {};
//BOOST_CLASS_VERSION(Klass, 3);
// would be expanded to:
namespace boost { namespace serialization {
template<>
struct version<Klass> {
static const int value = 3; // not defined anywhere
};
} }
int foo (int val) { // was used by old logger
return val;
}
int bar (const int &val) { // is used by new logger
return val;
}
int main () {
// return bar(boost::serialization::version<Klass>::value); // link error
return foo(boost::serialization::version<Klass>::value); // works fine
}
So there is no link error if member is used but not it's address is not queried. Passing value by reference qualifies as querying the address.

Why can't we have non-const class-level static variables?

Why does the Visual C++ compiler refuse to compile this code?
I obviously know that the error is:
Error C2864: Singleton<T>::p:
Only static const integral data members can be initialized within a class
but why? (i.e. is there a technical reason why it is not allowed?)
Is this compiler-specific behavior or is it mandated by the standard?
It seems to be fine at the global scope, so why not at the class scope?
It also seems like not all compilers mind this.
Also, what is the proper way of fixing this?
template<typename T>
struct Singleton
{
static T *p = 0; // Error C2864
static T *getInstance() { /*...*/ return p; }
};
That's standard behavior. Only static const integral members can be initialized without a proper definition. All other types need to be defined somewhere, and the initialization is written at the point of definition:
template<typename T>
struct Singleton {
static T *p;
static T *getInstance() { /*...*/ return p; }
};
template<typename T>
T *Singleton<T>::p = 0;
Objects must be defined somewhere. If you define them within your class then its defined at a header file, and you get a different object for each compilation unit that includes it. This is relaxed for const integral types and if you don't define them then the compiler just replaces it with its literal value. Taking the address of such static const integral would still result in a linker error if no definition is provided.
You can have this type of variable, but you cannot initialize it inside the class definition. The only type of variable that may be initialized the way you are asking is a static const.
A fixed version of your class definition removes the = 0, and adds this below the class definition:
template<typename T>
T *Singleton<T>::p = 0;
This is standard behavior. I'm not sure there is a technical reason, my guess would be it is for consistency with instance members (which cannot be initialized this way, either). Instance variables, of course, have constructor initializer lists instead.
As everyone pointed out, you cannot define a non-const, non-integral type in the body of a class (at least not with C++03, it changed with C++11 but I'm not sure how exactly). However, you can do it differently and clean.
template<typename T>
struct Singleton {
static T* getInstance() {
static T* p = NULL;
/*...*/
return p;
}
};
Declarations are meant to go into header files, where they will be compiled many times - each place they are included.
Static variables should only have one definition, so that only one copy will exist in the entire program. This means it needs to be in a source (.cpp) file. Assigning the value needs to be put at that spot.
Static constant integers are an exception to the above rules, because they can become compile time constants. When they're used, a literal value is substituted for the class member.
You cannot assign a non-static in the class body like that. Instead, assign the value outside the class (usually in your cpp file)
template<typename T>
struct Singleton {
static T *p;
static T *getInstance() { /*...*/ return p; }
};
template<typename T>
T *Singleton<T>::p = 0;
The interesting question is not the one you ask, but rather the opposite:
Why are const integral static members allowed to be assigned a value in the declaration?
The important bit of the question is declaration. The value that a variable gets is set in the variable definition, and that is consistent with non-const or non-integral static members of a class, where in the class you only offer a declaration. The initializer value is provided in the definition that is outside of the class definition, commonly in a .cpp that guarantees that it will be defined in a single translation unit.
But why can integral static constants have a value in the declaration?
For practical reasons. The compiler can use integral constants as compile-time constants, that is, it can actually replace the value of the constant in place of the identifier in all places where the constant is used as an rvalue, for example when defining the size of an array. But if the value is only present in the definition in a single translation unit, the compiler cannot possibly use it in all other translation units. As an example:
// fq.h
struct fixed_queue {
static const std::size_t max_elements; // [1]
int data[ max_elements ]; // Error: How big is data??
};
// fq.cpp
#include "fq.h"
const std::size_t fixed_queue::max_elements = 10;
If max_elements was not allowed to have a value in the declaration [1], then you would not be able to use that constant to define the size of the array data, which is a quite sensible use for a static constant.
Why not extend this to all other cases?
Because it does not seem to make much sense... Providing the value in the class definition cannot be used by the compiler in any other circumstance, and thus it is not needed, so only integral constants need to be treated differently.
The reason is that non-integral, non-const values require a memory location.
const int can be handled statically by the compiler and built directly into certain machine instructions, floats and more exotic objects need somewhere to live because the machine instructions will only operate in terms of their address.
In principle, the language could have allowed this, but it would mean either generating extra objects and bloating the binary (ok for consts), or making life hard for the compiler writers for non-consts: redundant copies would have to be dropped to preserve the one-definition rule (this is what template instantinations have to do, by the way).