C++ 17:在元组解包时只保留一些成员

Ant*_* C. 64 c++ tuples c++17

我们假设您需要调用以下方法:

std::tuple<int, int, int> foo();
Run Code Online (Sandbox Code Playgroud)

在C++ 17中,您可以调用该函数并在一行中解压缩元组:

auto [a, b, c] = foo();
Run Code Online (Sandbox Code Playgroud)

现在,我怎么能继续只存储b和c并丢弃a?

目前,我只知道两个选项:


1 - 我可以在自动解包时使用虚拟变量

但是,虚拟变量将被取消并且它将发出警告,因此如果我想要静默警告,那么代码将非常不愉快:

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

2 - 我可以存储整个元组并用于std::get检索对我需要的唯一变量的引用.代码不那么令人不愉快,但语法也不那么简单.

此外,对于我们希望保留在元组中的每个新值,此代码的大小增加一行.

auto tuple = foo();
int b = std::get<1>(tuple);
int c = std::get<2>(tuple);
Run Code Online (Sandbox Code Playgroud)

还有另一个更直接的方法来解包元组中的一些参数吗?

Man*_*uro 42

另一种选择是使用std::tie:

int b, c;
std::tie(std::ignore, b, c) = foo();
Run Code Online (Sandbox Code Playgroud)

编辑

正如评论中所提到的,这种方法存在一些问题:

  • 无法进行类型推断
  • 必须在之前构造对象,因此除非默认构造函数是微不足道的,否则它不是一个好的选择.

  • 这是非常方便的,但是使用这种方法不可能使用类型推断,这仍然是一个重要的缺点. (16认同)
  • 另一个缺点是您需要一些默认构造的对象实例,这些实例可能并不总是可能或最佳. (3认同)

Vit*_*meo 39

不幸的是,结构化绑定不明确支持丢弃成员,并且诸如[[maybe_unused]]不能应用于结构化绑定的属性(有一个提议:P0609:"结构化绑定的属性").

这是一个可能的解决方案:

auto [a, b, c] = foo();
(void) a; // unused
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一下,结构化绑定声明中标识符的正确术语是什么?例如,我在上面的例子中应该称之为"a"?"绑定"? (2认同)
  • 我认为没有具体的名称.标识符或"结构化绑定的名称"是标准所称的标识符? (2认同)

Bar*_*rry 21

您可以编写一个辅助函数,只返回某些索引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());
Run Code Online (Sandbox Code Playgroud)

或掉头或其他东西:

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());
Run Code Online (Sandbox Code Playgroud)

但是上面的实现几乎肯定会有一些终身复杂性问题直接使用结构化绑定不会 - 因为这里没有任何生命周期扩展.

所以,维托里奥说:

auto [a, b, c] = foo();
(void)a;
Run Code Online (Sandbox Code Playgroud)


Igo*_*tov 5

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

  • 如果使用结构化绑定声明中的至少一个结构化绑定,则不会为同一声明中的其他绑定发出"未使用的变量"警告.
  • 如果未使用结构化绑定声明中的任何绑定,则可以使用以下[[maybe_unused]]属性使警告静音:

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

  • @Alexander注意,在实践中你可能永远不需要第二个结构.如果您不使用任何绑定,为什么首先要有结构化绑定声明? (3认同)