Reference Behaviour in C++ - c++

Can I confirm with stackoverflow what I understand about reference in C++ is correct.
Let's say we have
vector<int> a;
// add some value in a
vector<int> b = a; // 1. this will result another exact copy of inclusive of a's item to be copied in b right?
vector<int> &c = a; // 2. c will reference a right? c and a both "point"/reference to a copy of vector list right?
vector<int> &d = c; // 3. d will reference c or/and a right? now a, c, d all reference to the same copy of variable
vector<int> e = d; // 4. e will copy a new set of list from d right (or you can say a or c)?
Thanks.

You're correct, b is a distinct copy of a, a/c/d are all the same thing, just accessible via different names.
And e is a copy of a/c/d.
If you duplicate that code using the int type rather than a vector, you can see by the addresses what's happening under the covers:
#include <iostream>
int main() {
int a = 7, b = a, &c = a, &d = a, e = d;
std::cout << "a # " << &a << '\n';
std::cout << "b # " << &b << '\n';
std::cout << "c # " << &c << '\n';
std::cout << "d # " << &d << '\n';
std::cout << "e # " << &e << '\n';
return 0;
}
The output of that is:
a # 0xbfaff524
b # 0xbfaff520
c # 0xbfaff524
d # 0xbfaff524
e # 0xbfaff51c
and you can see that a, c and d all have the same address while b and e are distinct.

Yep, looks right.
If you add elements to c or d, the new elements will also be reflected in a.
If you add elements to e, then only e will have them.

Related

How to comprehend "std::relational operators (shared_ptr) compares directly the stored pointers"

As per the documentation(http://www.cplusplus.com/reference/memory/shared_ptr/operators/), which says:
[emphasis mine]
The comparison compares directly the stored pointers (i.e., the value
the objects dereference to, and not their owned pointer (i.e., the
managed objects that are deleted on destruction), which may not be the
same in alias shared_ptr objects (alias-constructed objects and their
copies).
I can understand the example code in the documentation aforedmentioned to some degree.But I could not comprehend the quotes above.
Could somebody make it clear by giving some simple examlples and explain when using this operator makes sense?
I have been confused for a long time. I would be very grateful to have some help with this question.
Here is the example code which i can understand to some degree:
// shared_ptr relational operators
#include <iostream>
#include <memory>
int main () {
std::shared_ptr<int> a,b,c,d;
a = std::make_shared<int> (10);
b = std::make_shared<int> (10);
c = b;
std::cout << "comparisons:\n" << std::boolalpha;
std::cout << "a == b: " << (a==b) << '\n';
std::cout << "b == c: " << (b==c) << '\n';
std::cout << "c == d: " << (c==d) << '\n';
std::cout << "a != nullptr: " << (a!=nullptr) << '\n';
std::cout << "b != nullptr: " << (b!=nullptr) << '\n';
std::cout << "c != nullptr: " << (c!=nullptr) << '\n';
std::cout << "d != nullptr: " << (d!=nullptr) << '\n';
return 0;
}
Let's say, you have this struct:
struct Foo { int a, b; };
Then you can generate aliased shared pointers like this:
void f()
{
// create
shared_ptr<Foo> orig(new Foo{1, 2});
// aliased pointer 1, points into object held by orig
shared_ptr<int> pa(orig, &orig->a);
// aliased pointer 2, ditto
shared_ptr<int> pb(orig, &orig->b);
assert(pa != pb); // passes
}
Even though pa and pb share ownership of the same object (which they happen to share with orig as well), they carry different pointers, just like you would expect.
I post an answer which has been deleted by its creator. I think it's helpful to comprehend the example code in the question. Hope it helps to the one who see this question.
A shared_ptr points to an object that it manages. It is the one you get by shared_ptr::get. Expression a == b on shared_ptrs a and b tests whether a and b point to the same object, i.e. whether a.get() == b.get(). It returns true in this case. If a and b point to different objects, even if these two objects compared equal, the result is false.
Let's have a look on the sample code:
a = std::make_shared<int> (10);
b = std::make_shared<int> (10);
This creates two shared pointers pointing to separate integer objects. Even if these two integer objects have the same value 10, they are still different objects. So a.get() will be different from b.get(), and, consequently a==b returns false.
However, once you assign
c = b
the two shared_ptrs b and c will point to the same object, such that b.get() returns a pointer to the same object as c.get(). Hence, c == b returns true.
That a and b point to different objects, whereas b and c share the same object, can be illustrated as follows:
a = std::make_shared<int> (10);
b = std::make_shared<int> (10);
c = b;
*b = 30; // change the value of the managed object.
std::cout << *a << std::endl; // still prints 10;
std::cout << *b << std::endl; // prints 30;
std::cout << *c << std::endl; // prints 30, as it points to the same integral object as b
Hope it got clearer.

why structured bindings don't return references to struct members using `auto&` but the members themselves are returned

I thought that using structured bindings and auto& specifier I can obtain references to structure members and use them directly instead of going through the structure.
However, the following code works and the static asserts hold:
struct Test
{
int i;
char c;
double d;
};
Test test{ 0, 1, 2 };
auto& [i, c, d] = test;
i = 4;
c = 5;
d = 6;
// i, c, d are not references !
static_assert(!std::is_same_v<decltype(i), int&>);
static_assert(!std::is_same_v<decltype(c), char&>);
static_assert(!std::is_same_v<decltype(d), double&>);
cout << &i << " == " << &test.i << " (" << std::boolalpha << (&i == &test.i) << ")" << endl; // (true)
cout << test.i << ", " << (int)test.c << ", " << test.d << endl; // 4, 5, 6
But I thought C++ doesn't allow one variable to have more than one name except if one is the real variable and others are references but in this case the variable i is the same as test.i and neither of them is a reference.
In the array and type case, structured bindings aren't references - they're aliases to the corresponding members. The main reason for this is to support bitfields. You can't have a reference to a bitfield, but you can have an alias to one:
struct X {
uint8_t a : 2;
uint8_t b : 6;
};
void f(X& x) {
auto& [a, b] = x; // fine, a just means "x.a"
auto& a2 = x.a; // error
}
Separately from that, decltype() does something special on structured bindings - it will only ever give you a reference type if the member the binding refers to is a reference type, as in:
struct Y {
int& a;
int b;
};
void f(Y& y) {
auto& [a, b] = y;
// decltype(a) is int&, decltype(b) is int
}

C++ reference copying and assignment

I have few questions regarding below code
#include <iostream>
using namespace std;
class A
{
public:
A & add(A & b);
};
A & A::add(A & z)
{
A * a = new A();
A & b = *a;
cout << "Inside add, address of a: " << &a << endl;
cout << "Inside add, address of b: " << &b << endl;
cout << "Inside add, address of z: " << &z << endl;
A aa;
cout << "Inside, add, address of aa: " << &aa << endl;
return aa;
}
int main()
{
A *a = new A();
cout << "Original a: " << a << endl;
A & b = a->add(*a);
cout << "b: " << &b << endl;
return 0;
}
Q1. Inside main, line 3, a->add(*a), the same object pointed by pointer *a is passed. But inside the function A::add(A &), when i try to achieve the same effect via A &b = *a, i get a different object. Why is this so?
Q2. Inside A::add(A &), i return a non const reference to a local object aa and main gets the same memory address as the local reference. So this has the effect of extending the lifetime of local reference, beyond its scope.
Q3. Inside A::add(A &), i dereference *a multiple times, first via A &b = *a and then by return *a. In both cases, the memory address is always the same. How is this happening? You can check the output of &b inside A::add(A &)and the result of A &b = a->add(*a)
UPDATE:
The issue related to Q1 was that i was doing cout << &a, when i should have been doing cout << a
To eliminate return value optimization, i compiled with -fno-elide-constructors. I am using g++.
A1: You created a new *a with A* a = new A() The a in main is different than the a in A::add. The a in main is referenced by the variable z
A2: No, you created a on the heap, so it is going to last until you call delete on that variable
A3: A dereference does not change the memory location that is stored in the pointer, it just gets the value stored in that location. A reference is more like an alias. So &b is like saying &(*a)

Overloads of std::minmax() and std::tie

The std::minmax function introduced in C++11 is supposed to return a pair of respectively the lowest and the greatest of the given values.
In this example:
int a = 6, b = 5;
auto p = std::minmax(a, b);
std::cout << "p.first = " << p.first << std::endl;
std::cout << "p.second = " << p.second << std::endl;
This works as advertised and prints
p.first = 5
p.second = 6
Now I would like to effectively modify a and b to force b to be lower than a, as if running this code:
if (b > a)
std::swap(a, b);
So I have written this:
int a = 5, b = 6;
std::tie(b, a) = std::minmax(a, b);
std::cout << "a = " << a << std::endl;
std::cout << "b = " << b << std::endl;
However, this prints:
a = 5
b = 5
Using the initializer_list overload instead, like this:
std::tie(b, a) = std::minmax({a, b});
causes the expected result to be printed:
a = 6
b = 5
The compiler is GCC 4.7.1 (also tested with GCC 4.8.1 on Linux and Windows with Mingw-w64).
I have created an SSCCE at http://ideone.com/fA0qw5.
Why is the two-arguments overload not doing what it is supposed to do when using std::tie ?
The problem is that std::minmax returns references for the 2-argument version. That way, as soon as b in the std::tie(b, a) is assigned, the change is reflected in the b part of the returned std::pair, giving it the value of 5 - which in turn is assigned to the a in std::tie(b, a), yielding (5, 5) as the result.

C++ slicing caused by reference

I have problem to understand the behavior of the following code, which I modified from an example for C++ slicing:
#include <stdio.h>
#include <iostream>
struct B {
int x;
B() { x = 0; }
virtual void foo( const char* id ) {
std::cout << id << ": B=" << this << ", x=" << x << std::endl;
}
};
struct D1 : B {
int y;
D1() { x = 1; y = 100; }
virtual void foo( const char* id ) {
std::cout << id << ": D1=" << this << ", x=" << x << ", y=" << y << std::endl;
}
} d1;
struct D2 : B {
int z;
D2() { x = 2; z = 200; }
virtual void foo( const char* id ) {
std::cout << id << ": D2=" << this << ", x=" << x << ", z=" << z << std::endl;
}
} d2;
void main() {
std::cout << "d1 = " << &d1 << std::endl;
std::cout << "d2 = " << &d2 << std::endl;
std::cout << "By pointer at beginning: " << std::endl;
B* pb = &d1;
pb->foo( "pd1" );
pb = &d2;
pb->foo( "pd2" );
std::cout << "By Value: " << std::endl;
B b = d1;
b.foo( "d1" );
b = d2;
b.foo( "d2" );
std::cout << "By pointer after by value: " << std::endl;
pb = &d1;
pb->foo( "pd1" );
pb = &d2;
pb->foo( "pd2" );
std::cout << "By reference: " << std::endl;
B& rb = d1;
rb.foo( "rd1" );
rb = d2;
rb.foo( "rd2" );
std::cout << "By pointer after by reference: " << std::endl;
pb = &d1;
pb->foo( "pd1" );
pb = &d2;
pb->foo( "pd2" );
}
//The result is the following:
d1 = 0115B504
d2 = 0115B510
By pointer at beginning:
pd1: D1=0115B504, x=1, y=100
pd2: D2=0115B510, x=2, z=200
By Value:
d1: B=0036FE44, x=1
d2: B=0036FE44, x=2
By pointer after by value:
pd1: D1=0115B504, x=1, y=100
pd2: D2=0115B510, x=2, z=200
By reference:
rd1: D1=0115B504, x=1, y=100
rd2: D1=0115B504, x=2, y=100
By pointer after by reference:
pd1: D1=0115B504, x=2, y=100
pd2: D2=0115B510, x=2, z=200
From the above result, we can see that:
Value assignment causes slicing problem to the assign destination (b) by dropping the derived specific members, but leave the assign sources (d1 and d2) intact.
Reference assignment causes slicing to the assign destination (rd) by not assigning the derived specific members, thus partially changes the assign sources (d1 and d2).
At first, I was surprised that you can assign a reference to a different type (D2 to D1) via their base, until I realize the infamous C++ casting system. One conclusion seems that reference can only be initialized but not assigned.
We know that STL container for base object has slice problem because except list, all other STL container copy object around. It looks that STL container for base references should fair no better, unless it saves pointers inside.
How do you guys deal with this problem?
Thanks!
CP
I'm finding it hard to find a question in your text, but I'm going to assume it is something like:
"How do I polymorphically store objects in a standard container?"
First, let me just say that you can't store references in standard containers because you can't assign (rebind) them.
So the normal way is to have a container of base class pointers. If the container owns the items, then use smart pointers like unique_ptr or shared_ptr. If the container does not own the objects, then just use raw pointers.
B& rb = d1;
rb.foo( "rd1" );
rb = d2;
The last line is equivalent as if you wrote
d1 = d2;
except it applies only to base class subobject. In the first line you set up the reference creating rb as alias to d1 with different static type. The reference can not be reseated. The later assign targets the object. d1 directly could only be assigned with using trickery with its op=, but your changed static type makes Base::op= work.
Many mentors say you should only use abstract classes as base, and most of the reasoning points to accidents like this.
In hierarchies you rarely have the op= left, if absolutely needed you have some virtual ssign thing that verifies compatibility.