C++17: Keep only some members when tuple unpacking

[亡魂溺海] 提交于 2019-12-18 10:59:11

问题


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?


回答1:


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.



回答2:


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:


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;



回答4:


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();


来源:https://stackoverflow.com/questions/50176315/c17-keep-only-some-members-when-tuple-unpacking

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!