90

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?

9
  • 1
    If the warning is your main concern, I suppose you could try silencing it with [[maybe_unused]].
    – Baum mit Augen
    Commented May 4, 2018 at 13:42
  • 3
    @BaummitAugen well, Visual Studio seems to not care and to issue the warning at compile time anyway.
    – Antoine C.
    Commented May 4, 2018 at 13:46
  • 2
    This is explicitly talked about in the original proposal in section 3.7. And the "silence warnings" is explicitly mentioned as not enough of a use-case. Commented May 4, 2018 at 13:47
  • 1
    If you don't care about default-initializing your variable, you could use std::tie with std::ignore.
    – Holt
    Commented May 4, 2018 at 13:51
  • 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). It seems to me that MSVC should support both of these mechanisms, probably worth filing a request with them. Commented May 4, 2018 at 18:42

4 Answers 4

60

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.
3
  • 18
    This is quite convenient, however it is impossible to use type inference with this method, which is still an important drawback.
    – Antoine C.
    Commented May 4, 2018 at 16:50
  • 5
    Another drawback is that you need some default-constructed instances of the objects, which might not always be possible or optimal. Commented May 5, 2018 at 15:47
  • Yes, it is only convenient in a few cases
    – Mansuro
    Commented May 5, 2018 at 18:51
51

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
3
  • 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"? Commented May 4, 2018 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
    Commented May 4, 2018 at 13:53
  • @VittorioRomeo it's name of an lvalue. Commented May 6, 2018 at 15:46
24

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;
1
  • 1
    Returning a combination of the incoming tuple and a tie of the elements you want would emulate lifetime extension pretty well. Commented May 4, 2018 at 18:05
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();
6
  • 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? Commented May 4, 2018 at 21:06
  • Oh I missed that. "If none of the bindings"
    – Alexander
    Commented May 4, 2018 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
    Commented May 8, 2018 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
    Commented Oct 4, 2018 at 12:43
  • 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
    Commented Aug 12, 2022 at 14:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.