c++ 17:在元组拆包时只保留一些成员

时间:2021-09-13 00:34:24

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:

在c++ 17中,可以调用函数并将tuple解压缩为一行:

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

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

现在,如何只存储b和c而丢弃a?

Currently, I'm only aware of two options:

目前,我只知道两种选择:


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

1 -我可以在自动展开时使用哑变量

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.

2 -我可以存储整个元组并使用std::get来检索对我需要的唯一变量的引用。代码不那么令人讨厌,但语法也不那么直接。

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?

是否有另一种更简单的方法只在元组中解包一些参数?

4 个解决方案

#1


40  

Another alternative is to use an std::tie:

另一种选择是使用std: tie: 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


35  

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").

不幸的是,结构化绑定不显式地支持丢弃成员,并且不能将[[[[maybe_used]]]等属性应用于结构化绑定(有一个建议:P0609:“结构化绑定的属性”)。

Here's a possible solution:

这里有一个可能的解决方案:

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

#3


19  

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

您可以编写一个辅助函数,它只返回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


5  

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:

MSVC已经在v15.7预览版中修复了这个问题。最后的15.7版本将在未来几周内发布。这意味着所有主要编译器的最新版本支持的当前逻辑如下:

  • 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_used]]属性来保持警告。

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

#1


40  

Another alternative is to use an std::tie:

另一种选择是使用std: tie: 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


35  

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").

不幸的是,结构化绑定不显式地支持丢弃成员,并且不能将[[[[maybe_used]]]等属性应用于结构化绑定(有一个建议:P0609:“结构化绑定的属性”)。

Here's a possible solution:

这里有一个可能的解决方案:

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

#3


19  

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

您可以编写一个辅助函数,它只返回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


5  

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:

MSVC已经在v15.7预览版中修复了这个问题。最后的15.7版本将在未来几周内发布。这意味着所有主要编译器的最新版本支持的当前逻辑如下:

  • 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_used]]属性来保持警告。

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