2015-03-28 8 views
20

C'è un buon modo per usare std::tie e creare una nuova variabile in un colpo solo? In altre parole, se una funzione restituisce un std::tuple e vogliamo in definitiva suddividere il risultato in singoli componenti, c'è un modo per eseguire questi compiti senza definire preventivamente le variabili?Come creare una nuova variabile e usarla in std :: tie allo stesso tempo?

Ad esempio, si consideri il seguente codice:

#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 

} 

Fondamentalmente, la funzione example restituisce una tupla. Abbiamo già una variabile di tipo Bar che vogliamo assegnare, ma abbiamo bisogno di una nuova variabile di tipo Foo. Senza std::tie, non è necessario creare un'istanza fittizia di Foo, ma il codice ci impone di mettere prima tutto in un std::tuple e poi dividerlo. Con std::tie, dobbiamo assegnare prima un dummy Foo, ma non abbiamo un costruttore predefinito per farlo. In realtà, stiamo fingendo che i costruttori per siano complicati, quindi la creazione di un valore fittizio non è desiderabile. In definitiva, ci piacerebbe solo assegnare in entrambi foo e bar, ma voglio fare questo compito e allocare memoria per Foo allo stesso tempo.

+2

mi fa male leggere la lettura del codice, che è un segno sicuro che richiede una logica più semplice. La risposta breve è no. restituire una tupla di valori è un modo molto efficace per restituire e allocare. cravatta :: è lì solo per decomprimere i valori. Se si deve fare ciò, si consideri il collegamento a un 'boost :: opzionale ' –

+0

Il fatto che 'Bar' contenga un' Foo' sembra essere un'installazione per un errore. Una volta che divorzi (estrai) 'Foo' da' Bar', quel 50% di 'Bar' diventa" valido ma potenzialmente indeterminato/non altrimenti specificato ". – rwong

+0

Un modo per aggirare la necessità di separarli è per ogni altra classe che usava dividere un 'Foo' da una' Barra' per accettare semplicemente una 'Barra' e usare il suo membro' Foo', o per mantenere 'Barra'' vivo e passa intorno ai riferimenti a 'Foo'. – rwong

risposta

10

@ MikaelPersson aveva il collegamento giusto. Fondamentalmente, non c'è un ottimo modo per farlo. Tuttavia, ci sono alcuni modi intelligenti basati su N3802. Vale a dire, utilizzare

// 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{}); 
} 

Quindi, scrivere

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

E, sì, tutta questa cosa è brutto, ma la situazione è venuto in qualche caso ho avuto. Tuttavia, come mostra il link di @ MikaelPersson, questo è un problema generale e non ancora risolto.

16

Questa funzione è denominata structured bindings in C++ 17. Aggiunta molto gradita!

utilizzo Esempio:

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

testato in GCC 7.2 con -std=c++17.

+0

Funzionalità eccezionale. –

Problemi correlati