2

First I think the following is definitely Undefined Behavior:

Object & foo(){
    Object o;
    return o;
}

Object & ref = foo();

But now suppose a function that takes a reference of another object which will exists longer, and assign this reference to a local variable, and the local variable gets out of scope. Would the reference of this object also be destroyed as the local variable is destroyed?

class b {
public:
    int val;
    
    b& round() {
        val += 2;
        return *this;
    }

    int operator+(int i) const{
       return val + i;
    }
};

template<typename T>
class A {
public:
    typedef typename std::vector<b>::const_reference const_reference;
    typedef typename std::vector<b>::reference reference;

    vector<b> _a;
    A() {
        _a.resize(10);
    }

    inline const_reference set() const {
         return  _a.at(0);
    }

    inline reference set()  {
        return  _a.at(0).round();
    }
};

void func1(A<int>& a) {
    b tmp = a.set();
    tmp.val = tmp.val + 2;
    cout << a._a[0].val << endl;
}

int main() {
    A<int> a;
    a.set();
    func1(a);
    cout << a._a[0].val << endl;
}

Basically reference to _a[0] is assigned to tmp in func1.

I am doing it this way because I have concerns that _a[0] would be destroyed as tmp goes out of scope, But I want to return a reference so that I can use it as an lvalue, assigning rvalues directly to it, like doing

a.set() = 1;

Another point is that I am forced to write another set() const function and return a const reference.

I am forced to write the const keyword in another set because in another function, for example func2, I passed input as a const vector reference, but my usage is not modifying it. However, without the set() const method, the compiler errors when calling func2 (input,output).

No matching function for call to object of type 'const vector<A>.The argument has type const but the method is not marked const.

void func2(const vector<A>& input, vector<A>& output) {
    int sum = input[0].set() +2;
}

To me, things involving const rules become very tricky. So to solve this situation, I am thinking of copying input[0].set() to a local tmp first and do the addition.

So going back to the original question:

Would the local variable containing the reference destroy the reference itself when it goes out of scope? i.e. b tmp = a.set(); when tmp is out of scope from func1, nothing changes on a or a._a[0], or a._a[0] would also be released because tmp is a reference to a._a[0]?

Excuse me for being verbose... things are getting very complex and tough by the time I know and try to learn template programming and const of C++... So I am trying to have the flexibility to sometimes use the set() as a reference for lvalue assignment and sometimes I also want to use it just as an rvalue.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
GGinside
  • 63
  • 6
  • `Basically reference to _a[0] is assigned to 'tmp' in func1. I am doing it this way because I have concerns that _a[0] would be destroed as tmp goes out of scope,` This statement is very curious. Firstly just because one object gets destroyed does not mean that an object it has been copied from or refers to gets destroyed as well. Secondly even if you are concerned that `tmp` being destroyed would have an effect on `_a[0]` then why are you using `tmp` at all? – john Jun 24 '20 at 05:24
  • "the reference is destroyed" is nonsensical terminology – M.M Jun 24 '20 at 05:25
  • Your program seems OK, it's not clear to me what the question is exactly – M.M Jun 24 '20 at 05:28
  • 1
    _"First I think the following is definitely UB._" Actually no, if `ref` is never used. Simply binding an invalid reference is not UB. _Using_ an invalid reference is UB. On the next part, `b tmp = a.set();` makes a copy of the `b` object, so there is no UB. – cdhowie Jun 24 '20 at 05:30
  • @john using tmp just because I do not want to change the values in the reference.If i call a.set().round(), this would modify _a[0].val, which i do not want to do. In func1, i just want to make a tmp copy, and process it and return the processed data. But I am not sure about copying reference to a local variable, would the local variable destroy the original reference? By the definition of reference, is it a deep copy to the local variable or a shallow copy? – GGinside Jun 24 '20 at 05:37
  • @cdhowie this is actually a [a question i saw with an answer](https://stackoverflow.com/questions/9941502/c-reference-to-out-of-scope-object) – GGinside Jun 24 '20 at 05:38
  • @M.M excuse my wording. I do not know the real terminology here to use. – GGinside Jun 24 '20 at 05:40
  • like i said, i was experimenting so to add flexibility so that i could use set() to return lvalue and sometimes i could just use its rvalue without modifying its contents, and this is achieved by overloading the set function with const keyword. – GGinside Jun 24 '20 at 05:44
  • @GGinside Right, but all of the answers (except one) assume that you _use_ `ref`. If you don't use `ref`, there is no UB. – cdhowie Jun 24 '20 at 05:47
  • @cdhowie b tmp = a.set(); would create a local copy instead of a reference? that's all i wanna know..I want to confirm that when tmp is out of scope, nothing changes on a. – GGinside Jun 24 '20 at 05:49
  • basically the set() would server as a write port to the internal data, set() const is a read port. Only thing that confuses me right now is the local variable to contain the reference... – GGinside Jun 24 '20 at 05:57
  • @GGinside When you copy from a reference, the copy happens **exactly** the same as if you'd copied from the original object. The reference makes no difference to, nor is it affected by, this copy – john Jun 24 '20 at 06:03
  • https://www.meetup.com/StockholmCpp/messages/boards/thread/50183455 ...ok seems i totally not understanding reference copy in c++.. – GGinside Jun 24 '20 at 06:09
  • 1
    @GGinside That's talking about a completely different issue, given this code `int a = 1; int& b = a; int c = 2; b = c;` sometimes beginners think that the last assignment changes `b` so that it refers to `c`, but actually it assigns `c` to `a`, and `b` remains as a reference to `a`. That what it means when people say you can't *rebind a reference*. – john Jun 24 '20 at 06:21
  • @GGinside There is no such thing as a reference copy in C++. References refer (obviously) but copies happen the same way regardless. – john Jun 24 '20 at 06:23
  • @john thanks a lot. I was totally lost before. So I thought int a = 10; int& b = a; (b is the reference of a). If b changes, a change. I do int c = b, c = 20, i would get what i want : c is 20, but b and a are not changed, still 10, which is what i want in my case. b tmp = a.set() + 2; tmp is independent from _a[0]. That is all I wanna know... I thought int c = b would copy the reference, and if so, b would be out of scope when c is... but in reality, even i do func1(int& b) { int& c = b}; b would still be alive when c is out of scope..apparently i know nothing about reference copy – GGinside Jun 24 '20 at 06:30
  • _"I thought int c = b would copy the reference"_. Another way of thinking about this is to note that `c` is an `int`, not an `int&`, so it has to be a copy of whatever b refers to. And if you were to write `int&c = b` this is making another reference to `a`, not copying the reference. It might sound like semantics but there is a difference. – ChrisD Jun 24 '20 at 06:43

1 Answers1

4

It depends if the local variable within your function is defined as variable or as a reference. See the following example:

#include <iostream>

class Object{
public:
  Object(){};
  ~Object(){};
};

Object & foo(Object & obj){
    Object& o = obj;
    std::cout << &o << std::endl;
    return o;
}

Object & foo2(Object & obj){
    Object o = obj;
    std::cout << &o << std::endl;
    return o;
}

int main() {
  Object obj;
  std::cout << &obj << std::endl;
  Object & ref = foo(obj);
  std::cout << &ref << std::endl;
  Object & ref2 = foo2(obj);
  std::cout << &ref2 << std::endl;
}

which results in the following outpout:

# g++ -o main .\main.cpp && ./main
0x61feb7
0x61feb7
0x61feb7
0x61fe8f
0

If you create a local reference inside your function you will return a reference to the original object, which still exists after the function is left. On the other hand if you are doing a copy as in foo() of course you will return a reference of a variable which has gone out of scope and therefor has been destroyed.

If you want to modify an existing object you could go for a local reference. But if you want to to create a copy and modify it's values you better return it by move semantics. But make sure to really use move semantics instead of copy constructor. See the following example:

#include <iostream>

class Object{
public:
  Object(){};
  ~Object(){};
};

Object  foo(Object & obj){
    Object o = obj;
    std::cout << &o << std::endl;
    return o;
}

int main() {
  Object obj;
  std::cout << &obj << std::endl;
  Object ref = foo(obj); // move semantic
  std::cout << &ref << std::endl;
  Object ref2;
  ref2 = foo(obj); // copy constructor
  std::cout << &ref2 << std::endl;
}

with the following output

# g++ -std=c++11 -o main .\helloWorld.cpp && ./main
0x61febe
0x61febd
0x61febd
0x61febf
0x61febc

Edit
To answer the OP's questions from the comments I edited my answer.

You cannot update the "address" to an object a reference points to. That's by design of C++. See: Why are references not reseatable in C++

You can only update the value of an object a reference points to. See:

#include <iostream>

void foo(int& a){
  int b = 20;
  int&c = b;
  std::cout << "Address of C " << &c << " with " << c << std::endl;
  a = c; // does not copy the addres of C into a, but it's value
  std::cout << "Address of a " << &a << " with " << a << std::endl;
}

int main() {
  int a=10;;
  std::cout << "Address of a " << &a << " with " << a << std::endl;
  foo(a);
  std::cout << "Address of a " << &a << " with " << a << std::endl;
}

with the output:

Address of a 0x61febc with 10
Address of C 0x61fe88 with 20
Address of a 0x61febc with 20
Address of a 0x61febc with 20

If you want to change a pointer via a reference. Then the refrence also needs to hold a pointer. See:

#include <iostream>

void foo(int*& a){
  int b = 20;
  int* c = &b;
  std::cout << "Address of c " << &c << " pointing to " << c  << " with " << *c << std::endl;
  a = c; // copys the address stored in C into a. Now you have a possible memory leak.
  std::cout << "Address of a " << &c << " pointing to " << c  << " with " << *c << std::endl;
}

int main() {
  int * a = new(int);
  *a = 10;
  std::cout << "Address of a " << &a << " pointing to " << a << " with " << *a << std::endl;
  foo(a);
  std::cout << "Address of a " << &a << " pointing to " << a  << " with " << *a << std::endl;
}

Which results in the following output:

Address of a 0x61febc pointing to 0x10d1738 with 10
Address of c 0x61fe88 pointing to 0x61fe8c with 20
Address of a 0x61fe88 pointing to 0x61fe8c with 20
Address of a 0x61febc pointing to 0x61fe8c with 17635044 // since b got destroyed the address a holds does not contain the expected value

But you should really not do the later one. This will most likely cause a Memory leak (at least), undefined behaviour or open a black hole.

A.K.
  • 861
  • 6
  • 12
  • Hi A.K., thanks for your answer. I wasn't sure how to be articulate on this question.The thing that confuses me is not about returning a reference of a local variable that would definitely be out of scope, like you said you foo2. It is also clear in foo that the object lives if it is referenced by o.What i am confused about is the case when not returning. for example void func1(int& a) {int b = 10; a = b; } Now when b is out of scope, what happens to a? From my program, i see a still have b's value, assuming a was 0, now after func1 a is 10. – GGinside Jun 24 '20 at 21:21
  • I am confused because if the func1(int* a) { int b = 10; a = &b}, then when b is out of scope, a is junk memory now. But it seems that when it is a reference case, func1(int& a) {int b = 10; a = b; } when b is out of scope, a still reserves b's value. And I am not sure if a is a reference of b or b updated the a reference. From the discussion with John and search online, i am guessing that b updates a's reference in this case. – GGinside Jun 24 '20 at 21:32
  • Correct myself, "func1(int* a) { int b = 10; a = &b}, then when b is out of scope, a is junk memory now. " I think a is still what a points to before it enters func1. So i guess when having a function passing by pointer, the pointer type value is copied, meaning a update to the address is not updating the original address of a, but the local copy a's address.The reference case confuses me because I think a would become a reference of b. So when b is done, a is done. But the case is a is updated by b, instead of become b's reference, which is very interesting to me actually. – GGinside Jun 24 '20 at 21:53
  • This is what i am talking about that confuses me void func2(int& a) { int b = 20; int&c = b; a = c; } when b is out of scope, a is updated with c's value but not reference. – GGinside Jun 24 '20 at 22:00
  • 1
    func1(int& a) {int b = 10; a = b; } is not the same as func1(int* a) { int b = 10; a = &b}. It is more like func1(int* a) {int b = 10; *a = b;}. It is not allowed (by design) in C++ to change a reference. See: https://stackoverflow.com/questions/728233/why-are-references-not-reseatable-in-c – A.K. Jun 26 '20 at 05:44