【问题标题】:How to create a new variable and use it in std::tie at the same time?如何创建一个新变量并同时在 std::tie 中使用它?
【发布时间】:2015-06-01 10:59:53
【问题描述】:

有没有一种使用std::tie 并一次性创建新变量的好方法?换句话说,如果一个函数返回一个std::tuple,并且我们希望最终将结果分解为单独的组件,有没有办法在不事先定义变量的情况下进行这些赋值?

例如,考虑以下代码:

#include <tuple>

struct Foo {
    Foo(int) {}
};
struct Bar{};

std::tuple <Foo,Bar> example() {
    return std::make_tuple(Foo(1),Bar()); 
}

int main() {
    auto bar = Bar {};

    // Without std::tie
    {
        auto foo_bar = example();
        auto foo = std::get<0>(std::move(foo_bar));
        bar = std::get<1>(std::move(foo_bar));
    }

    // With std::tie
    #if 0
    {
        // Error: no default constructor
        Foo foo;
        std::tie(foo,bar) = example();
    }
    #endif

}

基本上,函数example 返回一个元组。我们已经有一个类型为Bar 的变量要赋值给它,但我们需要一个类型为Foo 的新变量。如果没有std::tie,我们不需要创建Foo 的虚拟实例,但是代码要求我们先将所有内容放入std::tuple,然后再进行分割。对于std::tie,我们必须首先分配一个虚拟Foo,但我们没有默认构造函数来执行此操作。真的,我们假装Foo 的构造函数很复杂,所以首先创建一个虚拟值是不可取的。最终,我们只想同时分配给foobar,但希望同时为Foo 分配内存。

【问题讨论】:

  • 读到代码我头疼,这肯定表明需要更简单的逻辑。简短的回答是否定的。返回值的元组是一种非常有效的返回和分配方式。 tie:: 只是用来解压值。如果您必须这样做,请考虑绑定到 boost::optional&lt;Foo&gt;
  • Bar 包含 Foo 的事实似乎是一个失败的设置。一旦您从Bar 中分离(提取)Foo,那 50% 的Bar 将变为“有效但可能不确定/未另行指定”。
  • 解决将它们分开的需要的一种方法是,用于将FooBar 分开的所有其他类只接受Bar 并使用其成员Foo , 或保持Bar 活着并传递对Foo 的引用。
  • 有一个关于这个问题的潜在提案的相关讨论,在这里查看:groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/…
  • 因为“内存分配”通常是指动态内存分配,所以我根据变量定义/创建重新表述了这个问题。我可以建议你减少你的例子吗?我认为很多东西是不必要的。我相信你所追求的本质可以归结为something like this

标签: c++ c++11 c++14


【解决方案1】:

此功能在 C++17 中称为 structured bindings。非常欢迎添加!

示例用法:

#include <iostream>
#include <tuple>

int main()
{
    auto tuple = std::make_tuple(1, 'a', 2.3);

    // unpack the tuple into individual variables declared at the call site
    auto [ i, c, d ] = tuple;

    std::cout << "i=" << i << " c=" << c << " d=" << d << '\n';

    return 0;
}

在 GCC 7.2 中使用 -std=c++17 测试。

【讨论】:

  • 很棒的功能。
【解决方案2】:

@MikaelPersson 有正确的链接。基本上,没有很好的方法可以做到这一点。不过,有一些基于 N3802 的巧妙方法。即,使用

// This comes from the N3802 proposal for C++
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template <typename F, typename Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
    using Indices =
        std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
    return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices{});
}

然后,写

// With compose
{
    auto foo = apply([&bar](auto && foo,auto && bar_) {
        bar=std::move(bar_);
        return std::move(foo);
    }, example());
}

而且,是的,这整件事很丑陋,但在我遇到的某些情况下确实出现了这种情况。不过,正如@MikaelPersson 的链接所示,这是一个普遍问题,尚未完全解决。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多