I would like to use a structured binding declaration to bring members of a struct into scope. I hope that the optimizer is able to remove the variables I don't actually use, but that's not my primary question. I got bitten by a destructor call I didn't expect:
#include <iostream>
using namespace std;
struct S {
int x = 0;
S() { cout << "S: " << this << endl; }
~S() { cout << "~S: " << this << endl; }
};
// Why does f() call ~S()?
void f(S const& s) {
auto [x] {s};
cout << "f: " << x << endl;
}
void g(S const& s) {
auto& [x] {s};
cout << "g: " << x << endl;
}
void h(S const& s) {
auto x = s.x;
cout << "h: " << x << endl;
}
int main(int, char**) {
S s;
f(s);
g(s);
h(s);
}
Running above program yields:
S: 0x7fff32c81778
f: 0
~S: 0x7fff32c81740
g: 0
h: 0
~S: 0x7fff32c81778
https://godbolt.org/z/cjeTnvx8G
The destructor call at the end of main() is expected. But why does f() call ~S()?
I've read the documentation on cppreference but apparently I don't understand C++'s inner workings well enough to spot why this causes a destructor call.
I am wording this not as a language lawyer, but hope to give some intuition how structured bindings work.
When you read
replace for a moment the
[key, value]part by an invented name:Now it is obvious that the code holds onto an instance of the value returned by
foo(). Furthermore, if there are any reference specifiers or cv qualifiers, they apply to the invented name. I.e.,becomes
and ditto for
&&. Reference and cv qualifiers do not apply to the nameskeyandvalue(but other rules achieve that they basically behave as if they were applied).The names
keyandvaluenow merely become aliases for the first and second structure members ofinvented_name(or the return values ofget<0>(invented_name)andget<1>(invented_name)).