Static object declaration only causing no error - c++

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.

Related

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.

Initialization of a static member inside a global function

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.

testing for storage location of static const member

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.

c++ class member of same type

I have the following situation:
class Foo
{
public:
static const Foo memberOfFoo;
........
}
So the thing is I can't initialize it in the same line where I declared it, and, I can't initialize it via Initializations List in the constructor, does anyone know what to do?
Put this outside of the class definition then:
const Foo Foo::memberOfFoo = whateverValue;
That is the definition of Foo::memberOfFoo, which can supply an initializer and has to go into the .cpp file (like any other definition of objects, it can only appear once in the whole program, otherwise you will get linker errors).
Sometimes you will find code that doesn't have definitions for its static data members:
struct A {
// sometimes, code won't have an "const int A::x;" anywhere!
static const int x = 42;
};
Omitting the definition like that is valid only if A::x is never address-taken and never passed to reference parameters. A more formal way to say when it is valid to omit the definition is: "When all uses of A::x immediately read the stored value of A::x". That's the case for many static integer constants.
Class statics other than constant integral types need to/can be initialized at the point of definition. You need to declare your (not so)memberOfFoo somewhere, by adding
const Foo Foo::memberOfFoo = /*construct here*/;
This is how you can implement initialization...
class Foo
{
public:
static const Foo memberOfFoo;
Foo(int, double)
{
...
};
};
const Foo Foo::memberOfFoo(42, 3.141592654);
...

using compile time constant throws error

in the below program i have used static const int init. But it is throwing error
/tmp/ccEkWmkT.o(.text+0x15d): In function check::operation()':
: undefined reference tocheck::init'
This error is coming only when used with vector. Can someone please help? what is the exact behaviour??
#include<vector>
#include<iostream>
using namespace std;
class check{
static const int init=1;
public:
check(){}
void operation();
};
void check::operation(){
vector<int> dummy;
dummy.push_back(init);
}
int main(){
check ck;
ck.operation();
}
"what is the exact behaviour?"
The problem is that push_back takes a reference parameter. You can use the value of a static const int member variable without providing a separate definition of the object, but you can't use a reference to the object itself (since it doesn't exist). The meaning of "using" the member itself is defined in the section of the standard on the One Definition Rule, 3.2/2.
One fix is to provide a definition in exactly one translation unit:
const int check::init;
If you do this, you can also choose to move the = 1 initialization from the declaration (inside the class) to the definition (outside the class).
Another fix is to create a temporary from the member variable (this only uses the value, it doesn't care where the object is located and hence doesn't care whether it exists), then pass a reference to the temporary:
dummy.push_back(int(init));
Of course there's a potential maintenance issue there, that if the types of init and dummy both change to, say, long long[*], and the value changes from 1 to something bigger than INT_MAX, then you're in trouble. For that reason you could use +init, since the unary + operator also creates a temporary for its result. Readers and future maintainers might be a bit puzzled by it, though.
[*] Supposing your implementation has long long.
You've to provide the definition of the static member outside the class (in .cpp file) as:
//check.h (same as before)
class check
{
static const int init=1; //declaration and in-class initialization
public:
check(){}
void operation();
};
Then in check.cpp file, do this:
//check.cpp
#include "check.h"
const int check::init; //definition
If you pass it by reference it is "used" and you may have to define in the .cpp file so that it gets an address.
If you just use the value of the constant, you might get away with not defining it.