Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do const references in structured bindings extend the lifetime of the decomposed object?

Does writing const auto& [a, b] = f(); guarantee extending the lifetime of the object returned from f(), or at least the objects a and b are bound to? Reading through the proposal I don't see anything obvious in the language to make me sure that it does unless it's just covered by something else. However, the following doesn't extend the lifetime of the temporary, so I don't see how it would be covered:

const auto& a = std::get<0>(f()); 

At the top of the paper it seems to suggest that it is covered

the cv-qualifiers and ref-qualifier of the decomposition declaration are applied to the reference introduced for the initializer, not for the individual member aliases

But in the proposed wording for the actual standard, the closest mention I see is below, though I'm not sure how to read it to get the guarantee I'm looking for:

if e is an unparenthesized id-expression naming an lvalue or reference introduced from the identifier-list of a decomposition declaration, decltype(e) is the referenced type as given in the specification of the decomposition declaration

It seems that gcc and clang both extend the lifetime of the object returned until the end of the scope based on a wandbox experiment. An uglier one implementing all the bells and whistles for my own type seems to extend the lifetime of the outer object and its other data members.

Though almost certainly the authors' intent(s), I'd like to know for sure that the language guarantees this is safe.

like image 443
Ryan Haining Avatar asked Dec 16 '16 02:12

Ryan Haining


People also ask

Does const reference extend lifetime?

You are not questioning why const references are allowed to bind to temporaries, but merely why they extend the lifetime of those temporaries. If the lifetime of the temporary returned by bar() were not extended, then any usage of a (exemplified by the line (1)) would lead to undefined behavior.

What is structured bindings?

Binds the specified names to subobjects or elements of the initializer. Like a reference, a structured binding is an alias to an existing object. Unlike a reference, a structured binding does not have to be of a reference type.


1 Answers

Yes. The trick is to realize that despite the appearance, the portion of a structured binding declaration before the [ doesn't apply to the names in the identifier-list. They apply instead to the variable introduced implicitly by the declaration. [dcl.struct.bind]/1:

First, a variable with a unique name e is introduced. If the assignment-expression in the initializer has array type A and no ref-qualifier is present, e has type cv A and each element is copy-initialized or direct-initialized from the corresponding element of the assignment-expression as specified by the form of the initializer. Otherwise, e is defined as-if by

attribute-specifier-seqoptdecl-specifier-seq ref-qualifieropte initializer ;

where the declaration is never interpreted as a function declaration and the parts of the declaration other than the declarator-id are taken from the corresponding structured binding declaration.

The names are then defined to either be aliases for the elements of e or references bound to the result of calling get on e.

In your example, it's as if by (assuming that f returns a two-element std::tuple):

const auto& e = f(); // 1 using E = remove_reference_t<decltype((e))>; std::tuple_element<0, E>::type& a = get<0>(e); std::tuple_element<1, E>::type& b = get<1>(e); 

(Except that decltype(a) and decltype(b) gets the special treatment to hide their referenceness.)

It should be pretty obvious that line #1 does extend the lifetime of f's return value.

like image 54
T.C. Avatar answered Oct 09 '22 04:10

T.C.