Below is a simple test on ebco, I compiled it on both vc9 and g++. The outputs differ on both compilers. What I want to know is that whether the behavior of vc is conformant.
#include <iostream>
class empty
{
};
class empty_one : public empty {};
class empty_two : public empty {};
class non_empty
: public empty_one
, public empty_two
{
};
int main()
{
std::cout << "sizeof(empty): " << sizeof(empty) << std::endl;
std::cout << "sizeof(empty_one): " << sizeof(empty_one) << std::endl;
std::cout << "sizeof(empty_two): " << sizeof(empty_two) << std::endl;
std::cout << "sizeof(non_empty): " << sizeof(non_empty) << std::endl;
std::cout << std::endl;
non_empty a[2];
void* pe10 = static_cast<empty*>(static_cast<empty_one*>(&a[0]));
void* pe20 = static_cast<empty*>(static_cast<empty_two*>(&a[0]));
std::cout << "address of non_empty[0]: " << &a[0] << std::endl;
std::cout << "address of empty of empty_one: " << pe10 << std::endl;
std::cout << "address of empty of empty_two: " << pe20 << std::endl;
std::cout << std::endl;
void* pe11 = static_cast<empty*>(static_cast<empty_one*>(&a[1]));
void* pe21 = static_cast<empty*>(static_cast<empty_two*>(&a[1]));
std::cout << "address of non_empty[1]: " << &a[1] << std::endl;
std::cout << "address of empty of empty_one: " << pe11 << std::endl;
std::cout << "address of empty of empty_two: " << pe21 << std::endl;
}
On vc,
pe20 == pe11. (test1)
Can two sub-objects of two objects have the same address? Is this conformant?
Besides,
pe20 >= &a[0] + sizeof(a[0]) (test2)
Can the address of an sub-object passes the end of an object ?
On g++, above two tests does not hold.
EDIT: In c++0x standard draft, 1.8/6,
Unless an object is a bit-field or a base class subobject of zero size, the address of that object is the address of the first byte it occupies. Two distinct objects that are neither bit-fields nor base class subobjects of zero size shall have distinct addresses
The standard requires that two objects have different address when they are neither bit-fields nor base class subobjects of zero size. But it doesn't require that two sub-objects of zero size cannot have same address. So test1 can be true ?
pe10 == pe11. Can two sub-objects of
two objects have the same address? Is
this conformant?
No, two different objects cannot have same address. If they've, the compiler is not Standard Complaint.
By the way, which version of VC++ you're using? I'm using MSVC++2008, and it's output is this:
I think, you meant pe20==pe11? If so, then this also is wrong, non-standard. MSVC++2008 compiler has bug!
GCC is correct; see the output yourself : http://www.ideone.com/Cf2Ov
Similar topic:
When do programmers use Empty Base Optimization (EBO)
pe10 == pe11. Can two sub-objects of two objects have the same address? Is this conformant?
Nopes!
Size of an empty class cannot be zero because in that case two objects of the same class would be having the same address which is not possible.
Similarly two sub-objects of two different objects cannot have the same address. Your compiler is not conformant. Change it!
Related
After compilation, what does the reference become, an address, or a constant pointer?
I know the difference between pointers and references, but I want to know the difference between the underlying implementations.
int main()
{
int a = 1;
int &b = a;
int *ptr = &a;
cout << b << " " << *ptr << endl; // 1 1
cout << "&b: " << &b << endl; // 0x61fe0c
cout << "ptr: " << ptr << endl; // 0x61fe0c
return 0;
}
The pedantic answer is: Whatever the compiler feels like, all that matters is that it works as specified by the language's semantics.
To get the actual answer, you have to look at resulting assembly, or make heavy usage of Undefined Behavior. At that point, it becomes a compiler-specific question, not a "C++ in general" question
In practice, references that need to be stored essentially become pointers, while local references tend to get compiled out of existence. The later is generally the case because the guarantee that references never get reassigned means that if you can see it getting assigned, then you know full well what it refers to. However, you should not be relying on this for correctness purposes.
For the sake of completeness
It is possible to get some insight into what the compiler is doing from within valid code by memcpying the contents of a struct containing a reference into a char buffer:
#include <iostream>
#include <array>
#include <cstring>
struct X {
int& ref;
};
int main() {
constexpr std::size_t x_size = sizeof(X);
int val = 12;
X val_ref = {val};
std::array<unsigned char, x_size> raw ;
std::memcpy(&raw, &val_ref, x_size);
std::cout << &val << std::endl;
std::cout << "0x";
for(const unsigned char c : raw) {
std::cout << std::hex << (int)c;
}
std::cout << std::endl ;
}
When I ran this on my compiler, I got the (endian flipped) address of val stored within the struct.
it heavily depend on compiler maybe compiler decide to optimize the code therefore it will make it value or ..., but as far i know references will compiler like pointer i mean if you see their result assembly they are compiled like pointer.
Consider, for example:
#include <array>
#include <iostream>
int main()
{
using Ram_bank = std::array<char, 0x2000>;
std::cout << "Size of ram bank is: " << Ram_bank::size() << '\n';
return 0;
}
Obviously this is not valid code, as Ram_bank is not an actual type or object but an alias. But, is there some way to achieve this? Is there a way to get the size of an aliased type?
Your code is not failing because Ram_bank is an alias. It is failing because size() is non-static and you would need an instance to call it on. Kosta's answer is an example of that.
Alternatively, you can use std::tuple_size:
std::cout << "Size of ram bank is: " << std::tuple_size<Ram_bank>::value << '\n';
You could instantiate an array and then take it's size (since std::array::size is a non-static member function). Every half-decent compiler should optimize this away:
std::cout << "Size of ram bank is: " << Ram_bank().size() << '\n';
In the following code I don't understand why 'Derived1' requires the same amount of memory as 'Derived3'. Also is there any specific significance of size of Derived 4 being 16.
#include <iostream>
using namespace std;
class Empty
{};
class Derived1 : public Empty
{};
class Derived2 : virtual public Empty
{};
class Derived3 : public Empty
{
char c;
};
class Derived4 : virtual public Empty
{
char c;
};
class Dummy
{
char c;
};
int main()
{
cout << "sizeof(Empty) " << sizeof(Empty) << endl;
cout << "sizeof(Derived1) " << sizeof(Derived1) << endl;
cout << "sizeof(Derived2) " << sizeof(Derived2) << endl;
cout << "sizeof(Derived3) " << sizeof(Derived3) << endl;
cout << "sizeof(Derived4) " << sizeof(Derived4) << endl;
cout << "sizeof(Dummy) " << sizeof(Dummy) << endl;
return 0;
}
The output of this code gave was:
sizeof(Empty) 1
sizeof(Derived1) 1
sizeof(Derived2) 8
sizeof(Derived3) 1
sizeof(Derived4) 16
sizeof(Dummy) 1
A class must have a sizeof of 1 or greater otherwise pointer arithmetic would break horribly, and the elements of array of that class would all occupy the same memory.
So sizeof(Derived1) is at least 1, as is sizeof(Empty). Empty base optimisation means that the size of the derived class is effectively zero.
sizeof(Derived3) can also be 1, since the single member is a char. Note that the compiler is again exploiting empty base optimisation here.
The polymorphic classes (i.e. those containing virtual keywords), have larger sizes due to your compiler implementing the requirements for polymorphic behaviour.
In the following code I don't understand why 'Derived1' requires the same amount of memory as 'Derived3'.
Unlike all other objects, empty base-sub-objects are allowed to share their address with their sibling objects. Therefore an empty base does not need to occupy any memory whatsoever.
Besides the empty base, Derived3 contains nothing more than a char object. The size of char is 1, so Derived3 fits within the size of 1.
Besides the empty base, Derived1 contains nothing. But because all objects (excluding exceptions involving sub-objects) must have a unique address, the minimum size of any type is 1. Therefore the size of Derived1 is 1.
Also is there any specific significance of size of Derived 4 being 16.
No significance whatsoever. The size is quite typical.
MemoryBlock(MemoryBlock&& other)
: _data(nullptr)
, _length(0)
{
std::cout << "In MemoryBlock(MemoryBlock&&). length = "
<< other._length << ". Moving resource." << std::endl;
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other._data = nullptr;
other._length = 0;
}
Here is an example from Microsoft msdn.
It's size_t and can't be free. So you don't have to assign it to 0.
Why set other._length to 0?
THX
For completeness sake, no specific technical reason as far as I know. other will be destroyed when exiting its scope, since you defined it as an rvalue reference in the move constructor.
You have to make sure to leave rvalue references in move constructors/assignments in a destructable state, which is the case by assigning nullptr to _data (in general you must assure that pointers point to null). The _length = 0 is just for completeness (or habit): The data is moved, data pointer is nulled and the length must be zero.
It's often a good idea to do that, so you can maintain a stricter invariant, making other parts of the class easier to reason about, and require less if checks. To take a concrete example, suppose MemoryBlock had a length() method that returned length. If you didn't set _length on move, then this length() method would require an if check on _data not being null. By setting it to zero after move, this method can just trivially return _length.
After you move the contents of an object A to another object B, A is still around. It will be destroyed at the end of the scope and, in the meantime, it can be used again. You can't really count on what the contents of A are after they have been moved, but you can assign a new value to it. For example, this is legitimate code:
std::string a = "Some string";
std::cout << "a before move: " << a << endl;
std::string b = std::move(a);
std::cout << "a after move: " << a << endl;
std::cout << "b after move: " << b << endl;
a = "Some other string";
std::cout << "a after reassignment: " << a << endl;
It produces this under Visual Studio:
a before move: Some string
a after move:
b after move: Some string
a after reassignment: Some other string
class Base1 {
int x;
};
class Base2 {
int y;
};
class Derive : public Base1, public Base2 {
public:
enum {
PTR_OFFSET = ((int) (Base2*)(Derive*)1) - 1,
};
};
But the compiler complains
expected constant expression
Everyone knows that the expression values 4 except the compiler, what goes wrong?
How, then, to get the offset at compile time?
Addressing the immediate compiler error you are seeing in the supplied code, (Base2*)(Derive*)1 will most likely become reinterpret_casts when compiled, and that as DyP wrote as a comment to the question is not a constant expression which is required for enumeration initialization. Some compilers, notably GCC are not as strict on this point and will allow for reinterpret_cast in constant expressions even though it is forbidden by the standard (for further discussion of this see the comments for this GCC bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49171 and Constexpr pointer value).
The broader question of identifying at compile time what the layout of an object is and the offsets to its various members is a tricky one without a well-defined answer. The standard give implementers a lot of latitude to pad/pack an object's fields, usually out of alignment considerations (for a good summary see http://www.altdevblogaday.com/2013/05/03/cc-low-level-curriculum-part-11-inheritance/). While the relative ordering of an object's field must be maintained, int y in an instance of Derived need not be at an offset of sizeof(x) from the start of the instance of Derived; the answer is compiler and target architecture dependent.
All of that being said, this sort of structure layout information is determined at compile time and at least on some compilers is made accessible (even if not in portable, standards compliant ways). In the answer to this question, C++ Compile-Time offsetof inside a template, Jesse Good provides some code that on GCC at least will allow one to determine field offsets within a type at compile time. This code will unfortunately not provide the correct offsets for base class members.
A good solution to your problem awaits implementation of compile time reflection support in C++, something for which there is ongoing work as part of a standards working group: https://groups.google.com/a/isocpp.org/forum/#!forum/reflection .
Here is an example that works on Clang.
The approach uses a builtin that is available on both Clang and GCC. I have verified Clang (see Code Explorer link below), but I have not attempted with GCC.
#include <iostream>
/* Byte offsets are numbered here without accounting for padding (will not be correct). */
struct A { uint64_t byte_0, byte_8; uint32_t byte_16; };
struct B { uint16_t byte_20, byte_24; uint8_t byte28; uint64_t byte_29; };
struct C { uint32_t byte_37; uint8_t byte_41; };
struct D { uint64_t byte_42; };
struct E : A, B, C, D {};
template<typename Type, typename Base> constexpr const uintmax_t
offsetByStaticCast() {
constexpr const Type* Type_this = __builtin_constant_p( reinterpret_cast<const Type*>(0x1) )
? reinterpret_cast<const Type*>(0x1)
: reinterpret_cast<const Type*>(0x1);
constexpr const Base* Base_this = static_cast<const Base*>( Type_this );
constexpr const uint8_t* Type_this_bytes = __builtin_constant_p( reinterpret_cast<const uint8_t*>(Type_this) )
? reinterpret_cast<const uint8_t*>(Type_this)
: reinterpret_cast<const uint8_t*>(Type_this);
constexpr const uint8_t* Base_this_bytes = __builtin_constant_p( reinterpret_cast<const uint8_t*>(Base_this) )
? reinterpret_cast<const uint8_t*>(Base_this)
: reinterpret_cast<const uint8_t*>(Base_this);
constexpr const uintmax_t Base_offset = Base_this_bytes - Type_this_bytes;
return Base_offset;
}
int main()
{
std::cout << "Size of A: " << sizeof(A) << std::endl;
std::cout << "Size of B: " << sizeof(B) << std::endl;
std::cout << "Size of C: " << sizeof(C) << std::endl;
std::cout << "Size of D: " << sizeof(D) << std::endl;
std::cout << "Size of E: " << sizeof(E) << std::endl;
/* Actual byte offsets account for padding. */
std::cout << "A offset via offsetByStaticCast<E, A>(): " << offsetByStaticCast<E, A>() << std::endl;
std::cout << "B offset via offsetByStaticCast<E, B>(): " << offsetByStaticCast<E, B>() << std::endl;
std::cout << "C offset via offsetByStaticCast<E, C>(): " << offsetByStaticCast<E, C>() << std::endl;
std::cout << "D offset via offsetByStaticCast<E, D>(): " << offsetByStaticCast<E, D>() << std::endl;
return 0;
}
Output:
Size of A: 24
Size of B: 16
Size of C: 8
Size of D: 8
Size of E: 56
A offset via offsetByStaticCast<E, A>(): 0
B offset via offsetByStaticCast<E, B>(): 24
C offset via offsetByStaticCast<E, C>(): 40
D offset via offsetByStaticCast<E, D>(): 48
Program ended with exit code: 0
Code available on Compiler Explorer: https://godbolt.org/z/Gfe6YK
Based on helpful comments from constexpr and initialization of a static const void pointer with reinterpret cast, which compiler is right?, particularly including the link to a corresponding LLVM commit http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20120130/052477.html
Recently I found that the code ((int) (Base2*)(Derive*)1) - 1 broke down when Derive : public virtual Base2. That is, the offset of virtual base is unknown at compile-time. Therefore, it it forbidden in c++ standard.