123

Prelude:

std::tuple<int, int, int> f();
std::tuple<int, int, float, int> g();

C++1z will introduce syntax for structured bindings which will make it possible to write instead of

int a, b, c;
std::tie(a, b, c) = f();

something like

auto [a, b, c] = f();

However, std::tie also allowed to specify std::ignore to ignore certain components, e.g:

std::tie(a, b, std::ignore, c) = g();

Will it be possible to do something similar using the new structured bindings syntax? How would it work?

jotik
  • 17,044
  • 13
  • 58
  • 123
  • 2
    Just put an arbitrary name there. – n. m. could be an AI Nov 18 '16 at 09:41
  • 1
    @n.m. won't an arbitrary name create a copy ? – Piotr Skotnicki Nov 18 '16 at 11:33
  • 1
    @Piotr Not more copies than with `std::ignore`, I think. Since we have guaranteed copy elision, the dummy variable is initialized; with `std::tie`, the temporary that is at the rhs of the assignment to `std::ignore` is initialized. – j6t Nov 18 '16 at 11:54
  • [p0144r2](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0144r2.pdf): "Symmetry with `std::tie` would suggest using something like a `std::ignore`. However, this feels awkward.". In spite of this, I think that there is a NB comment requesting it – metalfox Nov 18 '16 at 12:00
  • @PiotrSkotnicki hopefully it will be optimised away. – n. m. could be an AI Nov 18 '16 at 13:42
  • 2
    It would be possible to have a macro `auto[IGNORE]` that generates a unique name (ex: with compiler-specific __COUNTER__ or __LINE__). It would be readable enough, and in practice would function like `std::ignore` for `std::tie`. – KABoissonneault Nov 18 '16 at 14:56
  • I meant `__COUNTER__` and `__LINE__` – KABoissonneault Nov 18 '16 at 15:19
  • 2
    @PiotrSkotnicki No, the only copy a decomp declaration makes is the thing that's being decomposed. The things being declared are either aliases to the members/elements of that thing or references that binds to what `get` returns. – T.C. Nov 18 '16 at 20:23
  • 1
    See answer by @metalfox to [https://stackoverflow.com/questions/41404001/structured-binding-with-maybe-unused](https://stackoverflow.com/questions/41404001/structured-binding-with-maybe-unused). In essence: in a future standard, for structured bindings, compilers would only be allowed to warn about unused variables if *all* names introduced in the binding are unused. – Mike Kaganski Dec 03 '19 at 09:19

3 Answers3

83

The structured bindings proposal contains a dedicated section answering your question (P0144R2):

3.8 Should there be a way to explicitly ignore components?

The motivation would be to silence compiler warnings about unused names. We think the answer should be “not yet.” This is not motivated by use cases (silencing compiler warnings is a motivation, but it is not a use case per se), and is best left until we can revisit this in the context of a more general pattern matching proposal where this should fall out as a special case.

Symmetry with std::tie would suggest using something like a std::ignore:

tuple<T1,T2,T3> f();

auto [x, std::ignore, z] = f(); // NOT proposed: ignore second element

However, this feels awkward.

Anticipating pattern matching in the language could suggest a wildcard like _ or *, but since we do not yet have pattern matching it is premature to pick a syntax that we know will be compatible. This is a pure extension that can wait to be considered with pattern matching.

However, note that the working draft of the Standard is currently being revised by the relevant National Bodies (NB), and there is a NB comment requesting this feature (P0488R0, US100):

Decomposition declarations should provide syntax to discard some of the returned values, just as std::tie uses std::ignore.

metalfox
  • 6,301
  • 1
  • 21
  • 43
  • 21
    It’s too late now, but I would point out that a feature which feels awkward to use and will probably be replaced in the future is better than *not having the ability to use the feature at all*, and this doesn’t seem like the kind of thing that will make the standards committee wish for a time machine because there is no other reasonable interpretation of `std::ignore` in structured bindings. – Daniel H Aug 07 '17 at 15:45
  • 2
    @DanielH They have to balance between having numerous features to do the same thing (bloat, harder to teach, more error prone) or waiting with the penalty being people have some extra variables unused. As they said, ignoring parts of a structured binding will be doable when they introduce a generalized pattern matching (`inspect` has already been proposed and has a `is` and `as` keywords too). Instead of supporting `std::ignore` combined with whatever is added later, they will have just what they add later. That's a simplifying blessing in a complex language. – user904963 Mar 12 '22 at 05:25
13

Will it be possible to do something similar using the new structured bindings syntax?

No. You'll just have to make up a variable name that won't be mentioned later.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 38
    which will generate unused variable warning `-Wunused-variable` , you can use: `[[maybe_unused]] auto [ a, b, dummy ] = std::tuple(1,"2",3f);` but that means any of them might be unused, you won't know which one. there is no good solution for that case right now. hopefully it'll be improved in c++20. taken from here: https://stackoverflow.com/questions/41404001/structured-binding-with-maybe-unused?noredirect=1&lq=1 – serine Jul 01 '17 at 23:07
  • 8
    _"there is no good solution for that case right now"_: that's not completely true: [You can simply use `(void)dummy;` to get rid of the unused variable warning](https://stackoverflow.com/questions/1486904/how-do-i-best-silence-a-warning-about-unused-variables) without affecting the other variables. – andreee May 28 '19 at 10:37
  • 30
    @andreee: Using up a statement just to quiet a warning is not what I would call a "good solution". – Nicol Bolas May 28 '19 at 13:23
  • 4
    "Using up a statement just to quiet a warning..." Are we running out of statements? – AndyJost Jun 08 '20 at 17:12
  • 5
    @AndyJost: No, but we are running out of the amount of visual space on the screen. Spending it, particularly precious vertical space, on quieting a warning isn't useful. – Nicol Bolas Jun 08 '20 at 17:23
  • 1
    Another downside of this approach is that the lifetime of the unused variable will continue after the statement, and it will only get destroyed on leaving the scope. – jotik Mar 10 '21 at 19:45
  • 2
    @jotik: That was always going to happen. The "variable" is not a distinct object; it's the subobject of some other object. That "other object" will only be destroyed when leaving scope. You can't just destroy part of an object willy-nilly. – Nicol Bolas Mar 10 '21 at 19:48
  • Well, another obvious downside is that you'd need as many `dummy` variables as you have ignored bindings in the scope. This can get of of control. – SergeyA Oct 29 '21 at 14:25
  • 3
    @serine: Compilers aren’t supposed to issue warnings if some but not all structured bindings are unused ([dcl.attr.unused]/4). – Davis Herring Mar 06 '22 at 03:49
  • 1
    @SergeyA It's not going to get out of control. Just name them `discard1`, `discard2`, ... . You generally won't have that many structured bindings anyway or the situation is worse than just having a few dummy variables. Of course, they should solve the situation officially at some point, but the current situation isn't as dire as you're claiming. – user904963 Mar 12 '22 at 05:28
7

I usually use _ which is a valid identifier in C++ but looks the same as for example Kotlin's underscore operator which discards lambda parameters. You'd end up with a nice code sth like this

map([&](auto it) {
    auto [_, deviceServiceXAddr] = it;
    return deviceServiceXAddr;
});
foo
  • 574
  • 8
  • 13
  • 2
    I guess the downside of this approach is still that the lifetime of `_` will continue after the statement, and `_` will only get destroyed on leaving the scope. – jotik Mar 10 '21 at 19:46
  • 16
    Not to mention this will not work if you wish to ignore more than one variables. – Subhamoy S. Mar 15 '21 at 11:19
  • jotik> As someone pointed out in another comment, the lifetime of the container of variables you bind continues after the statement, so all its content too. Even if you were able not to bind some pieces, they would still be alive until the container lifetime ends. – spectras Apr 25 '21 at 21:26
  • 1
    auto [_,__,___,_______________,foo] = bar; =] – somebody4 Apr 28 '21 at 11:50
  • 12
    @somebody4 • `_______________` is a reserved identifier, and using it this way is **undefined behavior**. – Eljay Sep 01 '21 at 13:51
  • @Eljay Could you elaborate why using that identifier will cause UB instead of simply producing a compilation error? – 303 Jan 08 '22 at 00:32
  • 7
    @303 • all identifiers with a double-underscore anywhere in the identifier are reserved (in particular, for use by the compiler). Using them for your own code is UB. – Eljay Jan 08 '22 at 01:50
  • Also reserved, leading underscore followed by capital letter. Also reserved, although *in the global namespace* only: leading underscore followed by lowercase letter. – Eljay Jan 08 '22 at 02:11