It is known feature of C++ that a const reference extends the life time of the temporary object returned from a function, but is it acceptable to use constant reference to the member of the temporary object returned from the function?
Example:
#include <string>
std::pair<std::string, int> getPair(int n)
{
return {std::to_string(n), n};
}
int main(int, char*[])
{
const int x = 123456;
const auto& str = getPair(x).first;
printf("%d = %s\n", x, str.c_str());
return 0;
}
Output:
123456 = 123456
Yes, this code is perfectly acceptable. The rules, according to the standard are ([class.temporary]):
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when a default constructor is called to initialize an element of an array. If the constructor has one or more default arguments, the destruction of every temporary created in a default argument is sequenced before the construction of the next array element, if any.
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference...
As you can see the highlighted line makes it clear that binding reference to sub-objects is acceptable, as the complete object has to have its lifetime extended as well.
Note that first
does qualify as a subobject [intro.object]:
- Objects can contain other objects, called subobjects. A subobject can be a member subobject (9.2), a base class subobject (Clause 10), or an array element. An object that is not a subobject of any other object is called a complete object.
It's well defined.
From the standard: $12.2/6 Temporary objects [class.temporary]:
(emphasis mine)
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference
And about the subobect, $1.8/2 The C++ object model [intro.object]:
(emphasis mine)
Objects can contain other objects, called subobjects. A subobject can be a member subobject ([class.mem]), a base class subobject (Clause [class.derived]), or an array element. An object that is not a subobject of any other object is called a complete object.
first
is bound to reference and it's the member subobject of std::pair
, so the temporary std::pair
(i.e. the complete object) 's lifetime will be prolonged, the code should be fine.
For reference only: Clang and GCC say yes, VC says no.
As I mentioned in my comment:
The lifetime of the temporary should be prolonged as long as the lifetime of its member access (str in this case). That said, you should be just ok by taking return value by copy. RVO will avoid doing extra copies.
From the standard, section 12.2.5:
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
— A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.
— A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
To stay clear out of any trouble, I would rather do:
auto m_p = getPair(x);
This is as efficient as it can get because of RVO
which every compiler must be doing for this case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With