#include <iostream>
using namespace std;
class Apple
{
public:
int i;
string s = "HelloWorld";
string s1;
bool b;
};
int main(int argc, char *argv[]) {
Apple a; //doesn't this trigger default initialization??
cout << a.i << a.s << a.s1 << a.b;
}
If the object is a local variable, then the data members will be be default-initialised.
But here is the output: 0HelloWorld0. Isn't this value initialization??
Here's a simple test that indicates no value initialization is being performed in your code.
#include <iostream>
#include <string>
using namespace std;
class Apple
{
public:
int i;
string s = "HelloWorld";
string s1;
bool b;
};
int main()
{
{
Apple a;
a.i = 42;
a.b = true;
cout << a.i << a.s << a.s1 << a.b;
}
{
Apple a;
cout << a.i << a.s << a.s1 << a.b;
}
{
Apple a{};
cout << a.i << a.s << a.s1 << a.b;
}
}
Output:
42HelloWorld142HelloWorld10HelloWorld0
As you can see, in the second case those members still contain values from the previous assignment. Note that I'm not saying this is guaranteed or standard defined behavior.
In the third case you are indicating that the object should be value-initialized by adding the braces.
If your compiler doesn't support C++11's uniform initialization syntax, you can change it to the following to achieve the same effect.
Apple a = Apple();
Isn't this value initialization??
On my machine, your program outputs -1219700747HelloWorld244, a clear indicator for default initialisation.
That you get 0HelloWorld0 isn't fully random and can have many reasons. For example, it has something to do with your os architecture. All new memory for each program is initially zeroed, and this is why in this simple example, everything is set to zero. Or the compiler initializes the struct statically for performance reasons.
To quote the standard at 12.6.2/8:
In a non-delegating constructor, if a given non-static data member or base class is not designated by a
mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
— if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized
as specified in 8.5;
— otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
— otherwise, the entity is default-initialized (8.5).
Related
class Foo {
public:
int a = 1;
int b = 2;
};
int main() {
Foo foo;
cout << &Foo::a << endl;//output 1
cout << &Foo::b << endl;//also output 1
}
As we know pointers to member data should point out the relative offset from the start address of the object, but as the example shows, both pointers to Foo::a and Foo::b get a 1. Would anyone can explain what happened here?
First of all, a pointer-to-member is NOT required to be implemented as an offset from the "start address of the object" (a concept which is not part of the language standard). And indeed, certain types of member pointers couldn't be implemented like that.
What you're seeing, instead, is simply basic_ostream::operator<<(bool). Pointers to members can't be implicitly converted to many other types, but they can be converted to bool. The 1 you're seeing is simply an indication that you've passed a non-null member pointer.
Although the answer provided by Sneftel is correct, this is one way to view the "actual" (internal) value of the pointer-to-members:
#include <iostream>
struct x {
int a, b;
};
int main() {
int x::* pa = &x::a;
int x::* pb = &x::b;
std::cout << pa << ' ' << pb << '\n';
std::cout << *(int*)&pa << ' ' << *(int*)&pb << '\n';
}
Try it online!
This may have different values or cause undefined behavior, depends on the implementation. Also there is no guarantee that sizeof int == sizeof pa.
Most compilers support use of the offsetof macro defined in <cstddef>. Just beware the pitfalls, quoting cppreference :
If type is not a standard layout type, the behavior is undefined
(until C++17)use of the offsetof macro is conditionally-supported
(since C++17).
If member is a static member or a member function, the behavior is
undefined.
Example:
#include <iostream>
#include <cstddef>
class Foo {
public:
int a;
int b;
};
int main() {
std::cout << offsetof(Foo, a) << std::endl;
std::cout << offsetof(Foo, b) << std::endl;
}
Consider the following snippet:
#include <iostream>
union U{
U(): i(1) {}
int i;
int j = 2; // this default member initializer is ignored by the compiler
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
The code prints (see live example):
1
1
Where in the Standard does it say that the default member initializer for the member U::j is ignored by the compiler?
Note that the union below doesn't compile and this is OK according to [class.union.anon]/4. I was thus expecting the snippet above also not to compile.
See live example:
union U{
int i = 1;
int j = 2;
};
Where in the Standard does it say that the default member initializer for the member U::j is ignored by the compiler?
See [class.base.init] paragraph 9 bullet 9.1 in the C++17 CD.
N.B. your demo has undefined behaviour, because the active member of the union is i but you read from j. This works with some compilers as a non-standard extension, but is not allowed in ISO C++.
Note that you are declaring a union object, where all members share the same memory area - member variables turn into different "typed views" of the same data.
Thus, as members i and j are effectively stored in the same memory location, any initialization you perform on j (with the initializer) will be overwritten by your constructor setting i.
Just for test, remove the initialization of i from the constructor:
#include <iostream>
union U{
U() {}
int i;
int j = 2; // this initializes both i & j
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
The output would be
2
2
Update:
As per #Ayrosa comments and being just intrigued, I modified the original snippet to perform some initialization with a function (instead of a constant) to induce side effects.
#include <iostream>
int static someStatic()
{
std::cout << "Initializer was not ignored\n";
return(2);
}
union U{
U(): i(1) {}
int i;
int j = someStatic(); // this default member initializer is ignored by the compiler
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
Result was:
1
1
Meaning that the call to someStatic() was, in fact, ignored by the compiler.
This is about std::is_pod, which detects whether a template is a plain old data type or not.
See the following code:
struct A {
public:
int m1;
int m2;
};
struct B {
public:
int m1;
private:
int m2;
};
struct C {
private:
int m1;
int m2;
};
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_pod<A>::value << '\n'; // true
std::cout << std::is_pod<B>::value << '\n'; // false
std::cout << std::is_pod<C>::value << '\n'; // true
}
The 3 structs all look like POD to me. But apparently struct B is not.
I don't understand why. To me, they all have a trivial constructor, move and copy operator. Destructor is certainly trivial too.
I blame it on using 2 access specifiers, but I can't find information about this.
According to the standard (9 Classes [class], emphasis mine):
A standard-layout class is a class that:
...
— has the same access control (Clause 11) for all non-static data members,
...
and
A POD struct is a non-union class that is both a trivial class and a standard-layout class, and ...
Your hunch is correct, because B.m1 and B.m2 are both non-static and have different access control.
Trying to use a unique_ptr inside a union gives me a segfault when I try to std::move or std::make_unique it.
#include <iostream>
#include <memory>
union myUnion{
struct{std::unique_ptr<float> upFloat;}structUpFloat;
struct{std::unique_ptr<int> upInt;}structUpInt;
myUnion(){}
~myUnion(){}
};
struct myStruct{
int x;
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.x = 1;
bStruct.x = 2;
auto upF = std::make_unique<float>(3.14);
auto upI = std::make_unique<int>(3);
aStruct.num.structUpFloat.upFloat = std::move(upF);
bStruct.num.structUpInt.upInt = std::move(upI);
std::cout << "aStruct float = " << *aStruct.num.structUpFloat.upFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.structUpInt.upInt << std::endl;
return 0;
}
However, using a normal pointer works as expected:
#include <iostream>
#include <memory>
union myUnion{
struct{float *pFloat;}structPFloat;
struct{int *pInt;}structPInt;
myUnion(){}
~myUnion(){}
};
struct myStruct{
int x;
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.x = 1;
bStruct.x = 2;
auto upF = std::make_unique<float>(3.14);
auto upI = std::make_unique<int>(3);
aStruct.num.structPFloat.pFloat = upF.get();
bStruct.num.structPInt.pInt = upI.get();
std::cout << "aStruct float = " << *aStruct.num.structPFloat.pFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.structPInt.pInt << std::endl;
return 0;
}
This is using clang.3.4.2 or gcc.4.9.0. So I'm assuming that I am doing something wrong here. Any help would be appreciated.
EDIT:
Ok, so it's probably a nice thing to do to share the code I settled on. Big thanks to everyone who pointed me to managing the lifetime of my pointers in variant members using placement new.
#include <memory>
#include <iostream>
#include <vector>
struct myStruct
{
public:
union
{
std::unique_ptr<float> upFloat;
std::unique_ptr<int> upInt;
};
enum class unionType {f, i,none} type = unionType::none; // Keep it sane
myStruct(){}
myStruct(std::unique_ptr<float> p)
{
new (&upFloat) std::unique_ptr<float>{std::move(p)};
type = unionType::f;
}
myStruct(std::unique_ptr<int> p)
{
new (&upInt) std::unique_ptr<int>{std::move(p)};
type = unionType::i;
}
~myStruct()
{
switch (type)
{
case unionType::f: upFloat.~unique_ptr<float>(); break;
case unionType::i: upInt.~unique_ptr<int>(); break;
}
}
};
int main()
{
std::vector<std::unique_ptr<myStruct>> structVec;
structVec.push_back(std::make_unique<myStruct>(std::make_unique<float>(3.14f)));
structVec.push_back(std::make_unique<myStruct>(std::make_unique<int>(739)));
structVec.push_back(std::make_unique<myStruct>());
structVec.push_back(std::make_unique<myStruct>(std::make_unique<float>(8.95f)));
structVec.push_back(std::make_unique<myStruct>(std::make_unique<int>(3)));
structVec.push_back(std::make_unique<myStruct>());
for(auto &a: structVec)
{
if(a->type == myStruct::unionType::none)
{
std::cout << "Struct Has Unallocated Union" << std::endl;
}
else if(a->type == myStruct::unionType::f)
{
std::cout << "Struct float = " << *a->upFloat << std::endl;
}
else
{
std::cout << "Struct int = " << *a->upInt << std::endl;
}
std::cout << std::endl;
}
return 0;
}
Outputs:
Struct float = 3.14
Struct int = 739
Struct Has Unallocated Union
Struct float = 8.95
Struct int = 3
Struct Has Unallocated Union
Changing the active member of a union requires special care to object lifetime. The C++ Standard says (9.5p4):
Note: In general, one must use explicit destructor calls and placement new operators to change the active
member of a union.
When the members are plain old data, it generally "just works", even though you aren't calling constructors (using placement new) and destructors. That's because the lifetime for objects with trivial initialization begins "when storage is obtained" of sufficient size and correct alignment, and the union provides that.
Now you've got members with non-trivial constructor and destructor. Their lifetime doesn't begin when storage is obtained, you also have to cause initialization to finish. And that means placement new. Skipping destructor calls isn't safe either, you get undefined behavior if those destructors would have had side effects your program relies on (and a unique_ptr destructor has the side effect of deallocating its target).
Thus you are calling a move-assignment operator on a member whose lifetime hasn't begun. That is undefined behavior.
For unrestricted union, you have to manage yourself some construct/destruction.
Following may help:
union myUnion{
std::unique_ptr<float> upFloat;
std::unique_ptr<int> upInt;
myUnion(){ new (&upFloat) std::unique_ptr<float>{};}
~myUnion() {}
};
class myStruct
{
public:
~myStruct()
{
destroy();
}
void destroy()
{
switch (type)
{
case unionType::f: num.upFloat.~unique_ptr<float>(); break;
case unionType::i: num.upInt.~unique_ptr<int>(); break;
}
}
void set(std::unique_ptr<int> p)
{
destroy();
new (&num.upInt) std::unique_ptr<int>{std::move(p)};
type = unionType::i;
}
void set(std::unique_ptr<float> p)
{
destroy();
new (&num.upFloat) std::unique_ptr<float>{std::move(p)};
type = unionType::f;
}
public:
enum class unionType {f, i} type = unionType::f; // match the default constructor of enum
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.set(std::make_unique<float>(3.14f));
bStruct.set(std::make_unique<int>(3));
std::cout << "aStruct float = " << *aStruct.num.upFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.upInt << std::endl;
return 0;
}
In C++17, you may use std::variant instead of your own struct
From this reference:
If a union contains a non-static data member with a non-trivial special member function (copy/move constructor, copy/move assignment, or destructor) that function is deleted by default in the union and needs to be defined explicitly by the programmer.
I assume that the reason you wrapped the pointers in simple structures is because you could not build it otherwise, due to the restrictions imposed by the above paragraph.
What you have done instead is bypassed the compilers safety-guards, and probably have undefined behavior in your code.
From §12.6.2[class.base.init]/p8 of the standard (emphasis added):
In a non-delegating constructor, if a given non-static data member or
base class is not designated by a
mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
otherwise, if the entity is a variant member (9.5), no initialization is performed;
[...]
Union members are variant members, which means that the unique_ptrs are left uninitialized. In particular, no constructor, not even the default one, is called. Technically, the lifetime of these unique_ptrs never even began.
The unique_ptr move assignment operator must delete what the unique_ptr is currently holding, but you are move-assigning to an uninitialized "unique_ptr" containing garbage values. As a result, your move assignment likely caused an attempt to delete a garbage pointer, causing a segfault.
Does C++ do value initialization on simple POD typedefs?
Assuming
typedef T* Ptr;
does
Ptr()
do value-initialization and guarantee to equal (T*)0?
e.g.
Ptr p = Ptr();
return Ptr();
It does. For a type T, T() value-initializes an "object" of type T and yields an rvalue expression.
int a = int();
assert(a == 0);
Same for pod-classes:
struct A { int a; };
assert(A().a == 0);
Also true for some non-POD classes that have no user declared constructor:
struct A { ~A() { } int a; };
assert(A().a == 0);
Since you cannot do A a() (creates a function declaration instead), boost has a class value_initialized, allowing to work around that, and C++1x will have the following, alternative, syntax
int a{};
In the dry words of the Standard, this sounds like
The expression T(), where T is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized
Since a typedef-name is a type-name, which is a simple-type-specifier itself, this works just fine.
#include <iostream>
struct Foo {
char bar;
char baz;
char foobar;
// the struct is a POD
//virtual void a() { bar='b'; }
};
int main() {
Foo o1;
Foo o2 = Foo();
std::cout << "O1: " << (int)o1.bar <<" "<< (int)o1.baz <<" "<< (int)o1.foobar << std::endl;
std::cout << "O2: " << (int)o2.bar <<" "<< (int)o2.baz <<" "<< (int)o2.foobar << std::endl;
return 0;
}
Output:
O1: -27 -98 0
O2: 0 0 0
Adding () propagates initializer calls to all POD members. Uncomenting the virtual method changes output to:
O1: -44 -27 -98
O2: -71 -120 4
However adding destructor ~Foo() does not suppress the initialization, although it creates non-POD object (output similar to first one).