I have a
class A
{
private:
static const int b = 10;
public:
static void testReadOnly(int _b)
{
const_cast<int &>(A::b) = _b;
}
};
and I want to test whether the member variable b is initialized at compile time and therefore stored in the code-segment (read-only).
To do so, I try to change the value of b, which should produce some sort of runtime error (i.e. Segmentation fault, thrown by the MMU), if it actually is stored in the code-segment.
I supposed that above code should build, but my compiler/linker tells me undefined reference to 'A::b'
Why?
Put a definition for the static member, outside of class declaration to solve linkage errors:
class A
{
static const int b = 10;
...
};
const int A::b;
~~~~~~~~~~~~~~~
In addition, any modification of a constant value (by weird castings) will invoke undefined behavior.
Undefined behavior is a unknown behavior, sometimes causes to crash the application, sometimes not.
Related
I have this class with a function that returns a value. For complicated reasons, the value needs to be returned as a const reference.
(minimal working example contains an int array, real code has more complex objects, hence the reference)
class Foo
{
public:
static constexpr const int OUT_OF_BOUNDS_VALUE = -9999;
const int& ret(int i) const { return i < 0 || i > 4 ? OUT_OF_BOUNDS_VALUE : test[i]; }
private:
int test[5] = {0, 1, 2, 3, 4};
};
This gives me warning C4172: returning address of local variable or temporary in VS2015 and it doesn't even compile with GCC.
Adding the line constexpr const int Foo::OUT_OF_BOUNDS; outside of Foo lets GCC compile just fine. VS2015 still gives the warning.
Removing constexpr and splitting the declaration from the definition fixes the warning, but why should I have to do that?
OUT_OF_BOUNDS isn't local, and it isn't temporary, right? Does it not have an address when it is defined and declared inside of the class definition?
See the warning live: https://godbolt.org/z/fv397b9rr
The problem is that in C++11, we have to add a corresponding definition for a static constexpr declaration of a class' data member. This is explained in more detail below:
C++11
class Foo
{
public:
static constexpr const int OUT_OF_BOUNDS_VALUE = -9999; //THIS IS A DECLARATION IN C++11 and C++14
//other members here
};
In the above code snippet(which is for C++11,C++14), we have a declaration of the static data member OUT_OF_BOUNDS_VALUE inside the class. And so, in exactly one translation unit we have to provide a corresponding definition. Otherwise you'll get a linker error which can be seen here.
That is, in exactly one translation unit we should write:
constexpr const int Foo::OUT_OF_BOUNDS;//note no initializer
C++17
class Foo
{
public:
static constexpr const int OUT_OF_BOUNDS_VALUE = -9999; //THIS IS A DEFINITION IN C++17
//other members here
};
In the above code snippet(which is for C++17) we have a definition of the static data member OUT_OF_BOUNDS_VALUE inside the class. So since C++17, we don't have to provide the definition of OUT_OF_BOUNDS_VALUE anywhere else since we already have a definition for it inside the class.
The warning that you're getting with MSVC seems to be a bug.
What happens when you use the constexpr in your function is that a temporary local instance is created. You could try to leave out constexpr and static in the declaration of your static member OUT_OF_BOUNDS so you have a const object / instance you can get a reference of.
EDIT:
If you must have a static member, declare it const static int and put in the definition const int Foo::OUT_OF_BOUNDS = -1; in an appropriate place.
test[i] is an int, you're binding it to a reference to const int in the return statement. No?
The problem can be shown on a simple example:
extern A<bool> a;
B b(a);
A<bool> a("a");
In header file:
template<typename T>
class A {
public:
A(const char * name) : m_name(name) {}
const char * name() const {
return m_name;
}
...
...
private:
const char * m_name;
};
class B {
public:
B(A<bool> & a) {
printf("%s = %p\n", a.name(), &a);
}
};
The code actually compiles, links and produce:
(null) = 0044F604
Demo
I wonder if this shouldn't be caught by compiler and fail.
Compiler in question is gcc 9.2.0 (mingw-w64-i686).
In C++, global objects within the same translation unit are initialized in the order they are defined (though the order across TU's is undefined). See [basic.start.init]/2:
... Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit.
On the other hand, storage for objects of static storage duration is allocated at program loading time, so each global object has an address from the beginning.
In general in C++ it is allowed to reference a not-yet-initialized object, but accessing it is undefined behavior. Generally no diagnostic is required for undefined behavior.
In your case, B can be constructed with a reference to A, and it can save the passed-in reference to be used later:
class B {
A<bool> & a_;
public:
B(A<bool> & a) : a_(a) { }
void run() {
printf("%s = %p\n", a_.name(), &a_);
}
};
There is nothing wrong with passing around and using references to uninitialised objects. The standard explicitly allows this.
It is the programmer's responsibility to use such a reference in one of the limited ways permitted by the standard. The compiler is not required to complain if usage falls outside of the permitted range. Instead, the behaviour of a program that does this is undefined.
See basic.life/6 and basic.life/7 for detailed information.
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 the code is not causing compile error?
#include<iostream>
class x{
private:
int v;
public:
x():v(7){};
};
class b{
private:
static x as;
int a;
public:
b():a(8){};
};
//x b::as;
int main(){
b g;
return 0;
}
Even after commenting the particular line x b::as code works which I think it should as I haven't define/initiate my static object which is necessary. Why is it so?
And what if I initiate non-static object using static object like static x asd; x bv=asd; ?
The program compiles and runs fine because it doesn't actually violate any rules. The key rule here is, in [basic.def.odr]:
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used
in that program; no diagnostic required.
In your program, b::as is not odr-used yet. However, as soon as you use it somewhere (maybe you take its address, or try to access as.v, etc), then you violate this condition because you didn't provide a definition for as.
It wil not ever cause a error until you use it (b::as) in your code. Then, linker will not able to find it's definition and cause a unresolved reference error:
undefined reference to b::as
If you will try to initialize a non-static class member by static one, here is the same behaviour:
If you define your static member here is no error.
If you don't do that, there would be a unresolved reference error.
class b {
private:
static x as;
int a = b::as;
public:
b():a(8){};
};
x b::as; // this is now correct but if you comment this, you should can't initialize `b::a` with `b::as` then because it will cause a unresolved reference error.
My code looks like this:
class myClass{
public:
static int a;
};
void myFunc()
{
int myClass::a = 1;
}
I see the following compilation error
error C2655: 'myClass::a' : definition or redeclaration illegal in current scope
I make the following change and everything goes fine. Any idea?
class myClass{
public:
static int a;
};
int myClass::a = 1;
void myFunc()
{
}
Logically think like this:
If you never call myFunc(), myClass::a is not defined. So it must be in global scope.
In your first code snippet, potentially you may use myClass::a even without defining it, so it not allowed and former syntax is not valid C++.
Static variable must be initialized before program start, so if you initialized it in a function, there is chance that it will not be initialized at all. So the compiler should pose this as an error. Static variable is allocated at compile time (before program run). Hope this help.
If you define the static member data in a function, it's linkage is internal to the function. The linkage required for them is global.