2

Are there any articles about when it is a good practice to use const references as return types?

This is less of a question about a particular issue and more of an educative question.

We are in the process of migrating to modern C++ from a somewhat rudimentary level of C and me and my colleague are tasked with mentoring a group of other people on C++ since we are pretty comfortable with the language.

I am having a slight argument with said colleague about this subject. The example at hand is a simple class:

class Cls
{
private:
    vector<int> vec;

public:

    Cls() { /* put 3 random values in vec */ }

    const vector<int>& getVec() {return vec; }
};

My argument from the fact that:

  1. References should be used as returns since you don't lose time copy-ing stuff. It's straight-up speedup.

  2. References should be const since if you return just a reference to vec then anyone can mutate it using just the getter, which is obviously bad. Hence we need to return const TYPE& for getters.

My colleague supports just returning vector<int> and be done with it. His arguments are:

  1. It's an advanced concept that should not be taught from the start. We can just return by value from getters for now (and thus produce copies) and make the transition to reference later, when they are capable of understanding what happens behind the scenes. (My opinion being that since I consider using this to be very good practice it should be taught and enforced from the start so people get used to doing it, and then the part where we make sure everybody really understands what const references mean can come later since we might not have time to get the mentees to the level where can juggle references.)

So my questions basically are:

  1. Are there some articles about good practices on this subject and when to use const references instead of values as returns?

  2. While me and my colleague are fairly comfortable with C++ neither of us has worked professionally with it so... is there a standard or convention in the industry for this question?

Rares Dima
  • 1,575
  • 1
  • 15
  • 38
  • 1
    Several helpful answers here: https://stackoverflow.com/questions/21778045/c-return-value-reference-const-reference – Jose Jul 14 '20 at 09:34
  • 1
    It *may* be a good idea for class member function getters(like in your code). Other than that, it's almost never a good idea. – Waqar Jul 14 '20 at 09:35
  • 9
    Don't use getters for non-trivial data, and the question will resolve itself. Why should clients of `Cls` even know it holds a `vector`? If they need to iterate over data held by the vector, then `Cls` should provide such service, without exposing the vector itself. – n. m. could be an AI Jul 14 '20 at 09:36
  • 1
    @n.'pronouns'm. "Don't use getters for non-trivial data" is a rule I haven't heard of, yet. While I understand the reasoning for this example, could you provide any reference for the general application of this rule? I'm guessing you're referring to something like [this](https://stackoverflow.com/a/2977045) – eike Jul 14 '20 at 09:40
  • 5
    Your colleague is wrong if you actually want the `vector` and not a copy. Errors like this pop up here from time-to-time that look like this: `for (auto iter = object.get_vector().begin(); iter != object.get_vector().end(); ++iter)` and `get_vector` returns a copy, not a reference to the vector. – PaulMcKenzie Jul 14 '20 at 09:44
  • @PaulMcKenzie Thank you for the reply! I'm sorry if the question was no clear, we both know that in one case you return a copy and in one case you return a reference, and we both understand those mechanisms. The question was when is it a good idea to use that kind of references versus just returning copies. – Rares Dima Jul 14 '20 at 09:48
  • 1
    Returning references is also used in the [method chaining](https://stackoverflow.com/questions/1103985/method-chaining-why-is-it-a-good-practice-or-not) paradigm. For example: `object.get_options().set_x(2).set_y(4);` That type of syntax can only be achieved through returning references. – PaulMcKenzie Jul 14 '20 at 09:53
  • const ref return normally implies const function too: const vector& getVec() `const` {return vec; } – Cleiton Santoia Silva Jul 14 '20 at 10:09
  • Return a copy when you gona change it outside Cls class after returned, otherwise just return const ref. – Cleiton Santoia Silva Jul 14 '20 at 10:11
  • 1
    "It's an advanced concept" it's one of the most basic, fundamental C++ concept. Maybe consider some C++ training for your C programmers. "and make the transition to reference later" I cannot speak for you of your company but this is the perfect recipe for a forever temporary situation. – bolov Jul 14 '20 at 10:33
  • 1
    @eike it's an application of the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter). – n. m. could be an AI Jul 14 '20 at 10:34
  • @bolov well... that's the point, me and my colleague are that "C++ training" haha! Hence the debate on the "syllabus". – Rares Dima Jul 14 '20 at 11:08
  • Your colleague is right, ideally you should explain the simplest case first, and only then jump onto a more advanced topic. If you may not have time for that then that's another topic not related to C++ and coding at all. – Hack06 Jul 14 '20 at 11:28
  • 1
    Getters and setters in general are a red flag for not following the "tell, don't ask" principle (TDA). That principle would encourage to tell the object what you want to do, rather than treating the object as a data structure. Regardless, on my team the guidance is **always return the vector**, unless-and-until _profiling_ indicates it is a problem. Otherwise, it's premature optimization and more tightly couples the implementation to the surface API. – Eljay Jul 14 '20 at 11:40
  • @Eljay Thank you, this makes sense and it is helpful to get some assurance from people who are already working with the language and have some experience with it :) – Rares Dima Jul 14 '20 at 11:43

2 Answers2

5

The decision of when to return by-reference vs. by-value is not just a matter of performance, it's a matter of code semantics (although performance usually matters a lot when coding in C++).

Some notable examples of returning by-reference are:

  • a getter is often expected to return a const-reference to the actual member, unless that member is cheap to copy
  • to allow for method or operator chaining, returning a reference to the current object (*this)

The question of when to return by-reference actually boils down to a broader question of how to manage an object's lifetime safely. C++ Core Guidelines on object's lifetimes is a good resource to adhere to.

If the object being referred-to outlives the function invocation, then it's generally safe to return it by-reference.

So:

  • this, class members and objects with static storage duration: safe to return it by-reference
  • locals and function's input arguments: not safe to return by-reference

Regarding input arguments - it applies even to const references, since they can refer to temporaries. For example:

std::string const& badFunc(std::string const& arg) {
   return arg; // not a good idea
}

    std::string const& x = badFunc("abc");
    // now x contains a dangling reference
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • Thank you for the answer, this actually sheds some light! Since returning const refs is usually only a good idea when it comes to classes from what I see (such as my example from the question), in what cases should a getter return a const reference? Simply when the data is large and not trivially copyable? – Rares Dima Jul 14 '20 at 11:12
  • Yes, whenever an object is larger than 2..3 variables or involves a pointer to something allocated on the heap, then copying it would be inefficient. – rustyx Jul 14 '20 at 11:20
-1

First of all, you have to think about what you want to achieve. Do you actually need access to do the very instance of data or if the copies are acceptable for you. If the fist, then there is no other way, then using a reference or the pointer. But be aware, that the returned const reference doesn't guarantee that the pointed object wouldn't change. It only says that caller cannot modify it, but it can be changed inside the callee object.

Of course, there are some 100% valid usages for returning references, e.g. for making data streams.

b00rt00s
  • 114
  • 5