Подтвердить что ты не робот

Как создать новую переменную и использовать ее в 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 сложны, поэтому сначала создать фиктивное значение нежелательно. В конечном итоге мы хотели бы назначить как в Foo, так и Bar, но хотим выполнить это назначение и выделить память для Foo в то же время.

4b9b3361

Ответ 1

@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, это общая проблема, а не одна полностью разрешенная.

Ответ 2

Эта функция называется структурированные привязки в С++ 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;
}

Протестировано в GCC 7.2 с помощью -std=c++17.