0

Take the following snippet of code

 #include <iostream>
 #include <string>
  
 class Foo {
 private:
     std::string m_name;
     
 public:
     Foo(std::string name) : m_name { name } {}
     const std::string & get_name() const { return m_name; } 
 };
 
 int main() {
     Foo x { "bob" };
     x.get_name();
 }

Because I initialized an object and name exists somewhere in memory, is a temporary object is made when I call the x.get_name()? If a temporary object is made, than is there a point to returning by reference? My understanding is you return by reference so to avoid the cost of creating a large object or when using an std::ostream& object because you have to.

Happy Jerry
  • 164
  • 1
  • 8
  • The function returns a reference to the existing `name`, no temporary is created. – BoP May 23 '22 at 07:38
  • side note on naming: avoid having member variables and parameter (e.g in ctor) with same name, it's just a shortcut to problems and headaches. Pick a convention, e.g. trailing unserscore for member variable `name_` Compiler has no problem handling names an scopes. Human can have. To me, `: name { name } `is something I have to think about for a while. "Oh yes, it's using the `name` parameter value to initialize `name` member variable"; and it get much worse when you have long methods. "this `name` is the member variable, ain't it? oh no, it's the parameter of this method` – Gian Paolo May 23 '22 at 07:56
  • the returned value is ignored. The line `x.get_name();` does nothing – 463035818_is_not_an_ai May 23 '22 at 08:34
  • Adding to what Gian said: What would `Foo(std::string name) : foo{name}, name{name}, bla{name} { blub = name; }` do? Which `name` is used in each case? – Goswin von Brederlow May 23 '22 at 09:45

1 Answers1

4

Yes, there is a point, you return by reference if you want to return a reference to some object.

Why would you want to have a reference to some object? Exactly because you need to access it and not a copy of it. Reasons might vary, basic ones are that you do not want to make an extra copy - e.g. the get_name you posted, maybe you want to store it and access it later, and/or because you want to modify it.

Returning a reference is not much different from a passing parameter by reference.

No temporary std::string object is made in x.get_name(). The method returns lvalue reference by value. Since references are usually implemented as pointers, the true return value is a pointer. So a copy of the pointer is made during each call but that is like returning an int - can be done in registers or stack. So it's as cheap as it gets.

Yes, your understanding is correct, although I would say that const T& is used when we want to avoid copy for whatever reasons and T& should only be used when we need to get mutable access to the object - e.g. std::ostream& in operator<< which mutates the stream by printing into it.

BTW, you make an extra copy in your ctor - name parameter is copied into name member. Instead you should move it there like Foo(std::string name):name(std::move(name)){}.

Quimby
  • 17,735
  • 4
  • 35
  • 55
  • Getting the string by reference and then copying it to name member variable would be more efficient. Only a copy. Your approach is a copy to parameter of the constructor and then a move to name member. – digito_evo May 23 '22 at 07:51
  • @digito_evo Yes, but how do you get it by reference? `const std::string&`? Doesn't work for string literals as is in OP's example. `std::string_view` might be best here though. – Quimby May 23 '22 at 09:03
  • `const std::string&` definitely works for string literals. Because it can bind to an rvalue of the same type. Just go try it in [compiler explorer](https://godbolt.org/z/e6777r76T) and see it for yourself. And yes, the `string_view` version generates a smaller and more efficient code. – digito_evo May 23 '22 at 10:06
  • @digito_evo No it does not, compiler can just optimize short strings to elide the construction of `std::string` for inlined functions. [See for yourself](https://godbolt.org/z/5E4TY8jdc) in case of longer strings, there is `new` present because `std::string` must be created from the string literal. – Quimby May 23 '22 at 10:32
  • 1
    Oh right. Pass by reference does two allocations. Pass by value does a single allocation. – digito_evo May 23 '22 at 16:17