Sanparith Marukatat
Sanparith Marukatat

Reputation: 170

C++ reference to local temporary object

Consider the following template class

template <typename T>
class A {
public:
    virtual T const& getContent() = 0;
};  

I derive this class using 'int*' as type 'T', as follows

class A1 : public A<int*> {
private:
    int a;
public:
    int* const& getContent() { return &a; }
};

I got the following warning: 'returning reference to local temporary object'.

Questions:

  1. Does the compiler implicitly instantiate a local temporary object of type 'int * const' from '&a' before returning its reference?

  2. As I do know that A.a really exists, then can I just ignore this warning? Will there be any undesirable side-effects of using this code?

  3. What is the proper way of handling this situation? Do I need to work with the member variable 'int *a' instead. This would be cumbersome.

Upvotes: 2

Views: 1300

Answers (3)

3442
3442

Reputation: 8576

First of all...

int* const&

Is the same as...

int *const&

Edit: Originally, I wrongly stated it was equivalent to const int*&. Sorry, that was my fault.

And, references to pointers are more "cumbersome" than pointer themselves (at least IMHO), don't you think so? There's a reason pointers and references shall never be mixed (unless it's a consecuence of template instantiation, of course)...

int const&*

This is impossible, invalid, and last but no least, insane. Although there's no practical way for references to be implemented other than by pointers, they are not objects, in the sense that they (themselves) don't have a sizeof that does not equals that of their referenced type, and the fact that they don't have address at all, at least as far as the Sacred Writings of N4296 are concerned.

But... pointers are objects! The expression &a returns a rvalue (that is basically an object without an address) of type int*. Now, the statement return &a; will take that value and wrap it up in a reference, because...

  • That's what the function return type expects.
  • The lifetime of an rvalue of type T can be extended by means of it being holded in a reference of type const T&. However, you can't use this to return references to temporary rvalues.

Now, because all this stuff implies that you're returning a reference to a temporary rvalue in an unallowed way, you where tempted and falled into Undefined Behaviour.

The solution? Simply don't use references to pointers at all in the first place...

int const *getContent() { return a; }

Or, if you prefer it, use references, but not pointers...

int const &getContent() { return *a; }

Now, to the questions!

  • *Does the compiler implicitly instantiate a local temporary object of type 'int * const' from '&a' before returning its reference?*: Yes and no. It does instantiate such a temporary as explained above (but usually optimized away), but it's type is int*, not int *const, although the latter gets implicitly casted into the former.
  • As I do know that A.a really exists, then can I just ignore this warning? Will there be any undesirable side-effects of using this code?: That depends on whether you would ignore a divide-by-zero warning.
  • What is the proper way of handling this situation? Do I need to work with the member variable 'int *a' instead. This would be cumbersome.: You may either use "cumbersome" pointers or "beatiful" references, see above.

I hope this has led some light on you!

Upvotes: 0

Nir Friedman
Nir Friedman

Reputation: 17704

  1. Yes.
  2. You cannot ignore this warning. A.a exists, but that's not the issue. What you are returning is not a pointer to int, but a reference to a pointer to int, i.e. it is a double indirection. To be specific, a temporary int* was created inside getContent that pointed to A.a. A reference to that temporary was returned, and then the temporary was destroyed. Using the result of getContent will be undefined behavior.
  3. The idiomatic way to handle this situation would typically be to store the member you are passing a const reference to. In other words, have a member int* a, and then simply return a in your function. Returning a const reference is a common way to expose the fully functionality of a data member without allowing the user of the class to mutate it, messing up your class' invariants.

Upvotes: 7

Akhil Thayyil
Akhil Thayyil

Reputation: 9403

This is incorrect:

T const& getContent() = 0;

You can do:

T const getContent() = 0;
int* const getContent() { return &a; }

Upvotes: 0

Related Questions