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

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.

Related

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

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.

C++ equivalent of #define for integers

I am looking for a portable one line replacement for the #define in the following code. The replacement should hide the word APPLE in the namespace of the Foo object.
class Foo {
public:
#define APPLE 123
Foo(int a) : a_(a) { }
};
// elsewhere in another file
Foo f(APPLE);
I tried to make this more C++ friendly this, and it worked using the Intel 2017 compiler:
class Foo {
public:
static constexpr int APPLE = 123;
Foo(int a) : a_(a) { }
};
// elsewhere
Foo a(Foo::APPLE);
but it does not work with g++ ((GCC) 6.3.1 20170216), because it gives the error
undefined reference to Foo::APPLE
because it is probably trying to take a reference to APPLE.
I know I can "fix" the problem by creating definition in a *.cpp file of
constexpr int Foo::APPLE;
but that violates my ideal of having the #define be replaced by 1 line. My Foo class is header-file only, and now I would need a cpp file just for the definition of Foo::APPLE. I know I could also declare APPLE as a function (static constexpr int APPLE() {return 123;}) but that is a whole lot more typing in the declaration and at every point of use I need to call the function with ().
It seems easier just to use the #define and be done with it. Non-static const int works fine, but APPLE is not usable as an argument to the constructor. Maybe there is a good reason why this is impossible in C++.
Edit: This is not a duplicate of Undefined reference to static constexpr char[]. That question is related to a string and why the particular error message comes up. I am trying to avoid using static linkage all together (I acknowledge in my question that I am aware of how to do static linkage) and I want to do it a "better/cleaner" way, and I see from the Answers that the way that passes my criteria is to use enum.
You've already listed most alternatives in your question. You'll need consider which approach you want to take:
Use inline variable which requires C++17 (your first attempt works implicitly in this standard)
Define the static member in a source file, which you don't want to do
Use an inline static member function instead, which you also don't want
Use a namespace scoped constexpr variable instead of a member
Use a member enum: enum : int { APPLE = 123 };
Use the macro (don't pick this)
In addition to the things mentioned already, there's also "poor man's inline variables":
static constexpr const int& APPLE = std::integral_constant<int, 123>::value;
You define a class template with a constant static data member whose value is what you want. You define that static data member out-of-line - but in the header, since it's a static data member of a class template. In this case, std::integral_constant does all that already, so you don't have to write your own.
Then, you define your actual static data member constant as a constexpr reference to that class template static data member; no out-of-line definition is needed because it is not possible to odr-use a reference initialized by a constant expression.

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.

Static data member in an unnamed class C++

I read in the below link that unnamed(anonymous) class should not have static data memebers in it. Could anyone please let me know the reason for it?
https://www-01.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.cbclx01/cplr038.htm
says the below..
You can only have one definition of a static member in a program.
Unnamed classes, classes contained within unnamed classes, and local
classes cannot have static data members.
All static member data, if they are ODR-used, must be defined outside the class/struct.
struct Foo
{
static int d;
};
int Foo::d = 0;
If the class/struct is unnamed, there is no way to define the member outside the class.
int ::d = 0;
cannot be used to define the static member of an unnamed class.
Update for C++17
If you are able to use C++17 or newer, you may use
static inline int d = 10;
That will allow a static member variable to be defined in an anonymous class/struct.
Sample code to demonstrate that a static member variable need not be defined outside the class definition:
#include <iostream>
struct foo
{
static inline int d = 10;
};
int main()
{
auto ptr = &foo::d;
std::cout << *ptr << std::endl;
}
Command to build:
g++ -std=c++17 -Wall socc.cc -o socc
Output of running the program:
10
Thanks are due to #Jean-MichaƫlCelerier for the suggestion for the update.
Are you sure that the standard actually forbids this?
As mentioned the problem arises as you need to have an actual definition of the static member. The language provides for no method to define it. There is no other problems in referring to it as we can do it from within the struct or via an instance of it.
However GCC for example will accept the following:
static struct {
static int j;
} a;
int main() {
return a.j; // Here we actually refers to the static variable
}
but it can't be linked as a.j refers to an undefined symbol (._0::j), but there's a way to get around this. By defining it in assembler or by using compiler extensions you could. For example adding the line
int j asm("_ZN3._01jE") = 42;
Will make it work. _ZN3._01jE is the real mangled name of the static variable in this case, neither the mangled or unmangled name can be used directly as a identifier in standard C++ (but it can via GCC extension or assembler).
As you must probably realize this would only work with specific compilers. Other compilers would mangle the name in other ways (or even do other things that may make the trick not work at all).
You should really question why you would like to use this trick. If you can do the work using standard methods you should most probably chose that. For example you could reduce the visibility by using anonymous namespace instead for example:
namespace {
static struct Fubar {
static int j;
} a;
Fubar::a = 0;
}
Now Fubar is not really anonymous, but it will at least be confined to the translation unit.
When C++ was standardized, unnamed classes could not have static data members, as there was no way to define/instantiate them. However, this problem has been solved with C++11, as it added the decltype operator:
struct {
static int j;
} a;
// Declare the static member of the unnamed struct:
int decltype(a)::j = 42;
int main() {
return a.j == 42 ? 0 : 1; // Use the static member
}
So, in principle, there could be unnamed classes or structs with static data members. But the C++ standard makers deliberately did not allow this syntax, as the compiler doesn't know, which name it should give to that decltype(a)::j thing for linking. So most (all?) compilers - including current versions of GCC in normal mode - refuse to compile this.
In -fpermissive mode, GCC-9 und GCC-10 accept this code and compile it fine. However, if the declaration of a is moved to a header file, that is included from different source files, they still fail at the linking stage.
So unnamed classes can only be used inside a single translation unit. To avoid polluting the global namespace, just put anything, that needs to stay local, inside an anonymous namespace. So:
namespace {
struct whatever {
static int j;
} a;
int whatever::j = 42;
}
int main() {
return a.j == 42 ? 0 : 1;
}
compiles fine, doesn't pollute global namespace, and even doesn't lead to problems, if the name whatever clashes with a name from another header file.

struct method linking

I'm updating some old code that has several POD structs that were getting zero'd out by memset (don't blame me...I didn't write this part). Part of the update changed some of them to classes that use private internal pointers that are now getting wiped out by the memset.
So I added a [non-virtual] reset() method to the various structs and refactored the code to call that instead.
One particular struct developed an "undefined reference to `blah::reset()'" error.
Changing it from a struct to a class fixed the error.
Calling nm on the .o file h, the mangled function names for that method look the same (whether it's a class or a struct).
I'm using g++ 4.4.1, on Ubuntu.
I hate the thought that this might be a compiler/linker bug, but I'm not sure what else it could be. Am I missing some fundamental difference between structs and classes? I thought the only meaningful ones were the public/private defaults and the way everyone thinks about them.
Update:
It actually depends on the way the it's declared:
typedef struct
{
...
void reset();
} foo;
won't link.
struct foo
{
...
void reset();
};
links fine.
So, maybe just a lack of understanding on my part about the way typedefs work in this context?
I think that your problem (and I don't have a standards quote to back this up) is that because your struct doesn't have a name, your member function also does not have a globally identifiable name.
Although you're allowed to use a typedef name to introduce a member function definition, that member function must be part of a named type if you are going to be able to link it to a definition in a different TU.
typedef struct S_ { void reset(); } S;
void S::reset() // OK, but the function actually has id: S_::reset()
{
// ...
}
typedef struct { void reset(); } T;
void T::reset() // OK, defintion of anonymous struct's reset(),
// but this isn't an id that can cross TUs.
{
// ...
}
Edit: This could be a gcc bug, though.
7.1.3 [dcl.typedef] If the typedef declaration defines and unnamed class (...), the first typedef-name declared by the declaration to be that class type (...) is used to denote the class type (...) for linkage purposes only (3.5).
Edit:
Or gcc might be right. While the class has external linkage via its typedef name (3.5/4), a member function has external linkage only if the name of the class has external linkage. Although the class has external linkage and it has a name for linkage purposes only it is still an unnamed class, so it's member functions have no linkage.
Of course, the second declaration is the "proper" C++ way of doing things. Still, this links for me with g++ 4.4.1:
typedef struct {
void f() {}
} S;
int main() {
S s;
s.f();
}
I must confess that the whole struct namespace thing has always been one of the darker corners of C for me, but I'm sure someone else will come up with an explanation.
Edit: OK, minimalist code that reproduces the problem:
// str.h
typedef struct {
void f();
} S;
// str.cpp
#include "str.h"
void S :: f() {
}
// sm.cpp
#include "str.h"
int main() {
S s;
s.f();
}
Compiled with:
g++ sm.cpp str.cpp
Error:
undefined reference to S::f()
Now if someone can give us chapter and verse from the standard on why this doesn't work - obviously a struct namespace issue, I would have thought.