8

I feel like this question must surely be on SO somewhere, but either I can't figure out the right search terms or it was somehow missed.

Suppose I have class that protects its members like so...

class MyClass {
   int m_value;
public:
   MyClass(int v) : m_value(v) {}
   int value() const { return m_value; }
}

I've seen sample code all over SO that instead returns a const reference to the member variable like so...

class MyClass {
   int m_value;
public:
   MyClass(int v) : m_value(v) {}
   const int& value() const { return m_value; }
// ^^^^^^^^^^
}

I understand the value of returning const in general, and the value of returning references for composite objects, but what's the value for objects smaller than the architecture's pointer size? It seems both less efficient and less safe, with no advantage I can think of.


I suppose a similar discussion could be had for the constructor in my example, but this question is focused on returned values here.

This is the SO answer that prompted me to ask this question.

Phlucious
  • 3,704
  • 28
  • 61
  • 6
    Perhaps, in a more complete example, the user is expecting a reference in the sense that the referred value is expected to change and the user wants a reference to always know the current value but can't be allowed to change that value, – François Andrieux May 02 '18 at 14:37
  • Don't do that, 's all – n. m. could be an AI May 02 '18 at 14:37
  • @FrançoisAndrieux I hadn't thought of that, and interestingly what you describe as a potential feature is described as an "unsafe" liability in [this answer](https://stackoverflow.com/a/31914316/1666676) of a related(ish) question. – Phlucious May 02 '18 at 15:18
  • 1
    @Phlucious that remark applies to pretty much any feature of any language ever ;) – Quentin May 02 '18 at 15:21
  • @Phlucious I can be a liability if you aren't counting on it. It just depends on what you are trying to do. Using the wrong tool for the job always comes with a cost. – François Andrieux May 02 '18 at 15:24
  • Depends if you want the caller to be able to bind a reference to m_value. If m_value is effectively immutable, then it does not matter (other than for performance concerns, which others have expressed). If m_value can change over time, then the caller's binding will reflect the current state. – Eljay May 02 '18 at 16:00
  • If you want to keep the semantics of public data members, but want to restrict access to `const`, then you *need* a reference. Performance considerations shouldn't really come into it. The desired semantics is what matters. – juanchopanza May 02 '18 at 16:33
  • @Eljay I believe the point about always reflecting the current state to be the best answer to my question. Repost as an answer and I'll accept. – Phlucious May 02 '18 at 19:09
  • Design wise returning a const reference is (arguably) bad, because you are now leaking a private implementation detail in the public API. It is close to impossible to change the private storage to something else than an int. – André May 02 '18 at 19:42

3 Answers3

4

Why return a const reference to small class members?

Depends if you want the caller to be able to bind a reference to m_value.

MyClass myObj;
int const& keep_an_eye_on_you = myObj.value();
cout << "Was: " << keep_an_eye_on_you << endl;
myObj.doSomething(); // say this changes the value held in m_value
cout << "Now: " << keep_an_eye_on_you << endl;

If m_value is effectively immutable, then it does not matter (other than for performance concerns, which others have expressed).

If m_value can change over time, then the caller's binding will reflect the current state, as in the example above.

Eljay
  • 4,648
  • 3
  • 16
  • 27
2

Returning a const & to a type like int is probably completely pointless and possibly degrading to performance. The same thing can be said for passing const int& as a function parameter type, but that's a little more acceptable in that it does defeat unwanted type conversions at a calling site.

It's merely a poor habit that the programmer has gotten themselves into.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
2

If you talk just about int, then yes, it may be pointless. But when you know that return or parameter type can be changed later and you use an alias it can be useful to put const &, so later all you have to do is to change your alias value, which can be more than 8 bytes. Like this

class MyClass {
   using TInnerType = int;
   TInnerType m_value;
public:
   MyClass(const TInnerType& v) : m_value(v) {}
   const TInnerType& value() const { return m_value; }
// ^^^^^^^^^^
}
NuPagadi
  • 1,410
  • 1
  • 11
  • 31
  • Indeed, but with some metaprogramming, you could still have the `int` case return by value, and more complex classes return by const ref. – Bathsheba May 02 '18 at 14:48
  • @Bathsheba Yes, but why?) If there is no real performance difference between passing 4 and 8 bytes in your application (which is the common case, you know), why complicating your code? – NuPagadi May 02 '18 at 14:51
  • +1. Valid scenario but overcomplicated (imho) unless maybe if `MyClass` is a template and `value()` returns the template param. – Phlucious May 02 '18 at 23:37