Getting size of aliased std::array? - c++

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';

Related

What happens after C++ references are compiled?

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.

use an empty {} to initialize a vector is different?

I see some people tend to initialize a vector with an empty {}, and I wonder whether it is different from directly initialize with the default constructor?
for example:
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> vec;
vector<int> vec2 {};
cout << sizeof(vec) << " " << sizeof(vec2) << endl; // 24 24
cout << vec.size() << " " << vec2.size() << endl; // 0 0
}
and I check its assembly code, and it shows that initializing a vector with an empty {} generate more code(https://godbolt.org/z/2BAWU_).
Assembly code screen shot here
I am quite new to C++ language, and I would be grateful if someone could help me out.
Using braces is value initialization. Not using them is default initialization. As somebody alluded to in the comments, they should generate the exact same code when optimizations are turned on for vector. There's a notable difference with built-in types like pointers and int; there default initialization does nothing, while value initialization sets them to nullptr and zero, respectively.

Little problem with pointers and constants in C++

Can you explain this behaviour to me, pls? Here the code:
int* b = new int;
const int MAX_AGE = 90;
b = (int*)&MAX_AGE;
std::cout << b << std::endl;
std::cout << &MAX_AGE << std::endl;
std::cout << *b << std::endl;
std::cout << MAX_AGE << std::endl;
std::cout << "........." << std::endl;
*b = 2;
std::cout << *b << std::endl; // HERE I get 2, that's ok
std::cout << MAX_AGE << std::endl; // HERE I still get 90, why?
std::cout << b << std::endl;
std::cout << &MAX_AGE << std::endl;
The problem is that you lied to your compiler, and compilers are pretty good at exacting revenge on people who lie to them.
Specifically, on this line you told the compiler that MAX_AGE is changeable:
b = (int*)&MAX_AGE;
This is a lie, because you declared MAX_AGE to be a const. What happens next is called undefined behavior (UB): the compiler is free to produce any results, including complete nonsense, after your code triggers UB.
In your case, however, there is a pretty good explanation of what gets printed: knowing that MAX_AGE is another name for 90, the compiler has optimized std::cout << MAX_AGE << std::endl; to print 90, without looking up its value from memory.
MAX_AGE is declared as const int. With your c-style cast you remove constness and then proceed to modify a const value. This is UB.
This is a prime example for why this is UB: Due to the constness of MAX_AGE the compiler knows that it won't change and can thus replace all occurences of it by the literal 90.
const tells the compiler that the variable MAX_AGE should be stored in the write protected region of the respective segment in memory.
Armed with this knowledge the compiler can obviate the need to repeatedly read the same memory location. In other words the compiler might cache the constant value. That is why you see MAX_AGE showing up with the original value.
Anyway as has been already mentioned you shouldn't be confusing the compiler with your actual intentions. If you intend to store a variable in a write protected region then you shouldn't be modifying it.

Define variable type

I'm trying to convert a working define which prints the argument into a proper function, but I'm unsure of the variable type.
The working method I have is:
#define WARN(x) std::cout << "WARNING! " << x << "!" << std::endl;
Which I can then pass a sentence similar to using std::cout
WARN("This is a test warning." << " And this is some more stuff...");
However, I feel this should be more neatly wrapped into a function rather than using a define.
Please could you help me understand how the preprocessor is interpreting the variable x, and how I can translate the define into a function.
P.S. I am aware that I could use a variadic function, but I'd rather not have to pass the number of arguments and would rather avoid variadic altogether anyway.
but I'm unsure of the variable type.
That's where use of function templates can provide a clean solution.
template <typename T>
void WARN(T const& x)
{
std::cout << "WARNING! " << x << "!" << std::endl;
}
is a good replacement for
#define WARN(x) std::cout << "WARNING! " << x << "!" << std::endl;

empty base class optimization

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!