web-dev-qa-db-ja.com

新しい変数を作成し、それをstd :: tieで同時に使用するにはどうすればよいですか?

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にメモリを割り当てたいと考えています。

27
wyer33

@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のリンクが示すように、これは一般的な問題であり、まだ完全には解決されていません。

12
wyer33

この機能は、C++ 17では 構造化バインディング と呼ばれます。追加を歓迎します!

使用例:

#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;
}

-std=c++17を使用してGCC7.2でテストされています。

28
fireboot