84

Let's imagine you need to call the following method:

std::tuple<int, int, int> foo();

In C++17, you can call the function and unpack the tuple in a single line:

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

Now, how can I proceed to store only b and c and to discard a?

Currently, I'm only aware of two options:


1 - I can use a dummy variable when auto-unpacking

However, the dummy variable will be unused and it will issue a warning, so if I want to silent that warning the code will be quite unpleasant to see:

#pragma warning(push)
#pragma warning(disable:4101)
// ReSharper disable once CppDeclaratorNeverUsed
auto [_, b, c] = foo();
#pragma warning(pop)

2 - I can store the whole tuple and use std::get to retrieve the reference to the only variables I need. The code is less unpleasant but the syntax is also less straightforward.

Moreover, this code's size increases by one line for each new value that we want keep in the tuple.

auto tuple = foo();
int b = std::get<1>(tuple);
int c = std::get<2>(tuple);

Is there another and more straightforward method to unpack only some parameters in a tuple?

Antoine C.
  • 3,730
  • 5
  • 32
  • 56
  • 1
    If the warning is your main concern, I suppose you could try silencing it with `[[maybe_unused]]`. – Baum mit Augen May 04 '18 at 13:42
  • The title asks about keeping one element while the body of the question asks about keeping 2. – François Andrieux May 04 '18 at 13:46
  • 2
    @BaummitAugen well, Visual Studio seems to not care and to issue the warning at compile time anyway. – Antoine C. May 04 '18 at 13:46
  • 2
    This is explicitly talked about in [the original proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0144r0.pdf) in section 3.7. And the "silence warnings" is explicitly mentioned as not enough of a use-case. – Some programmer dude May 04 '18 at 13:47
  • @Lovy Looks like that doesn't apply anyway, according to the answer. That was just a guess on my part. – Baum mit Augen May 04 '18 at 13:49
  • 1
    If you don't care about default-initializing your variable, you could use `std::tie` with `std::ignore`. – Holt May 04 '18 at 13:51
  • I know that clang doesn't issue a warning if you use at least one of the bindings. Issuing warnings that the language doesn't have a fix for is not really great IMO – Rakete1111 May 04 '18 at 18:11
  • 4
    @Lovy Note that ReSharper C++ does support `[[maybe_unused]]` applied to a structured binding declaration, as do clang and GCC. Clang and GCC also suppress the "not used" warning if at least one of the structured bindings in the entire declaration used - I'll implement the same logic in ReSharper C++ ([RSCPP-22313](https://youtrack.jetbrains.com/issue/RSCPP-22313)). It seems to me that MSVC should support both of these mechanisms, probably worth filing a request with them. – Igor Akhmetov May 04 '18 at 18:42
  • @Lovy Actually [here](https://developercommunity.visualstudio.com/content/problem/113023/maybe-unused-with-structured-binding-still-causes.html) is the requst about `[[maybe_unused]]` support. Looks like it has been implemented in VS 15.7. – Igor Akhmetov May 04 '18 at 18:46

4 Answers4

57

Another alternative is to use an std::tie:

int b, c;
std::tie(std::ignore, b, c) = foo();

Edit

As mentioned in the comments, there are some issues with this approach:

  • No type inference possible
  • The objects must be constructed before, so unless the default constructors are trivial, it's not a good alternative.
Barry
  • 286,269
  • 29
  • 621
  • 977
Mansuro
  • 4,558
  • 4
  • 36
  • 76
48

Unfortunately structured bindings do not explicitly support discarding members, and attributes such as [[maybe_unused]] cannot be applied to structured bindings (there's a proposal for that: P0609: "Attributes for Structured Bindings").

Here's a possible solution:

auto [a, b, c] = foo();
(void) a; // unused
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 2
    BTW, what's the correct terminology for the identifiers inside the structured bindings declaration? E.g. what should I call `a` in the example above? A "binding"? – Vittorio Romeo May 04 '18 at 13:50
  • 2
    I don't think there's a specific name. Identifiers, or "names of structured bindings" is what the standard calls them? – Barry May 04 '18 at 13:53
  • @VittorioRomeo it's *[name of an lvalue](http://eel.is/c++draft/dcl.struct.bind)*. – Mário Feroldi May 06 '18 at 15:46
23

You could write a helper function that only gives you back certain indices of a std::tuple:

template <size_t... Is, typename Tuple>
auto take_only(Tuple&& tuple) {
    using T = std::remove_reference_t<Tuple>;

    return std::tuple<std::tuple_element_t<Is, T>...>(
        std::get<Is>(std::forward<Tuple>(tuple))...);
}

auto [b, c] = take_only<1, 2>(foo());

Or drops the head or something:

template <size_t... Is, typename Tuple>
auto drop_head_impl(Tuple&& tuple, std::index_sequence<0, Is...> ) {
    return take_only<Is...>(std::forward<Tuple>(tuple));
}

template <typename Tuple>
auto drop_head(Tuple&& tuple) {
    return drop_head_impl(std::forward<Tuple>(tuple),
        std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
}

auto [b, c] = drop_head(foo());

But the above implementations almost certainly have some lifetime complexity issues that directly using structured bindings won't - since there isn't any lifetime extension here.

So just, do what Vittorio says:

auto [a, b, c] = foo();
(void)a;
Barry
  • 286,269
  • 29
  • 621
  • 977
7

MSVC has already fixed this in VS 15.7 Preview. The final 15.7 release should be available in the coming weeks. This means that the current logic supported by the latest releases of all major compilers is as follows:

  • If at least one of the structured bindings in a structured binding declaration is used, no "Unused variable" warning will be issued for other bindings in the same declaration.
  • If none of the bindings in a structured binding declaration are used, it is possible to silence the warning by using the [[maybe_unused]] attribute:

    [[maybe_unused]] auto [a, b, c] = foo();
Igor Akhmetov
  • 1,807
  • 13
  • 15
  • 3
    @Alexander Note that in practice you'd probably never need the second construct. If you don't use any of the bindings, why do you have the structured binding declaration in the first place? – Igor Akhmetov May 04 '18 at 21:06
  • Oh I missed that. "If *none* of the bindings" – Alexander May 04 '18 at 22:14
  • @igorakhmetov RAII types are declared but never used ;) (I'm not sure if a function returning multiple RAII types is a good idea) – lakshayg May 08 '18 at 21:51
  • I do not believe the first bullet is correct. At least in this context: `for ([[maybe_unused]] auto [hash, state] : uniqueStates)`. `hash` is definitely unused but even with `[[maybe_unsed]]` I still get an unused variable warning with both GCC and MSVC. – Addy Oct 04 '18 at 12:43
  • I take the other approach: I disable `unused variable` warnings since one of the compiler's core jobs is to optimize that sort of thing for me. The more it does for me, the more I get done, the more value I bring. – jstine Nov 03 '19 at 15:27
  • 1
    @jstine I believe that warning is not there for optimization concerns. Think about the case you refactored a code and there are unused variables left. It is well possible that you missed something in that case, or at least they are confusing artifacts now. – Burak Aug 12 '22 at 14:54