2016-07-15 333 views
7

Utilizzando una struttura modello come many di seguito, è possibile restituire un set fisso di oggetti possibilmente non mobili e riceverli utilizzando il C++ 17 binding strutturato (auto [a,b,c] = f(); dichiara le variabili ab e c e assegna il loro valore da f restituendo ad esempio una struct o una tupla).Restituzione di aggregati variadici (struct) e sintassi per il modello variadic C++ 17 'construction deduction guide'

template<typename T1,typename T2,typename T3> 
struct many { 
    T1 a; 
    T2 b; 
    T3 c; 
}; 

// guide: 
template<class T1, class T2, class T3> 
many(T1, T2, T3) -> many<T1, T2, T3>; 

auto f(){ return many{string(),5.7, unmovable()}; }; 

int main(){ 
    auto [x,y,z] = f(); 
} 

Come illustrato in queste due domande e risposte (Do std::tuple and std::pair support aggregate initialization? e soprattutto la accepted answer by ecatmur, anche Multiple return values (structured bindings) with unmovable types and guaranteed RVO in C++17), std::tuple non supporta l'inizializzazione aggregata. Ciò significa che non può essere utilizzato per conservare e restituire tipi non amovibili. Ma una semplice struct come many può fare questo, che porta alla domanda:

E 'possibile creare una versione variadica di many che funzioni con un numero qualsiasi di argomenti?

Aggiornamento: in una versione di many, sarà consentita la seguente sintassi di guida?

template<typename Args...>  
many(Args...) -> many<Args...>; 
+0

Nota che c'è una proposta da qualche parte per sg come 'auto std :: tie (a, b, c) = ...'. – lorro

+0

@lorro IMO è una cattiva idea: rende la funzionalità del linguaggio di base come funzione di libreria. Per non parlare del fatto che il compilatore dovrà risolvere potenziali "utilizzi", accordi con lo spazio dei nomi e altre cose. –

+0

@Revolver_Ocelot: Penso anche che potrebbe essere fatto meglio, ma per alcuni motivi diversi/aggiuntivi: vale a dire, non è ortogonale ad altre caratteristiche. Il mio modo preferito per farlo sarebbe quello di consentire la dichiarazione delle variabili * ovunque *, inclusa la posizione dei parametri di funzione. L'altro problema che ho è che scriverò 'a = ...', mentre quello che intendi è chiamare il * costruttore * di 'decltype (a)', che sono due cose molto diverse. – lorro

risposta

6

In C++ 17 l'inizializzazione degli aggregati sarà in grado di inizializzare le classi di base pubbliche. Quindi puoi usare l'ereditarietà + espansione del pacchetto per costruire tale classe. Per farlo funzionare con attacchi strutturati, si dovrà esporre l'interfaccia parametri: specializzarsi std::tuple_size e std::tuple_element e fornire get la funzione per la classe:

//Headers used by "many" class implementation 
#include <utility> 
#include <tuple> 

namespace rw { 
    namespace detail { 

    template <size_t index, typename T> 
    struct many_holder 
    { T value; }; 

    template <typename idx_seq, typename... Types> 
    struct many_impl; 

    template <size_t... Indices, typename... Types> 
    struct many_impl<std::index_sequence<Indices...>, Types...>: many_holder<Indices, Types>... 
    {}; 

    } 

template <typename... Types> 
struct many: detail::many_impl<typename std::make_index_sequence<sizeof...(Types)>, Types...> 
{}; 

template<size_t N, typename... Types> 
auto get(const rw::many<Types...>& data) -> const std::tuple_element_t<N, rw::many<Types...>>& 
{ 
    const rw::detail::many_holder<N, std::tuple_element_t<N, rw::many<Types...>>>& holder = data; 
    return holder.value; 
} 

} 

namespace std { 
    template <typename... Types> 
    struct tuple_size<rw::many<Types...>> : std::integral_constant<std::size_t, sizeof...(Types)> 
    {}; 

    template< std::size_t N, class... Types > 
    struct tuple_element<N, rw::many<Types...> > 
    { using type = typename tuple_element<N, std::tuple<Types...>>::type; }; 
} 

//Headers used for testing 
#include <iostream> 
#include <string> 

int main() 
{ 
    rw::many<int, std::string, int> x = {42, "Hello", 11}; 
    std::cout << std::tuple_size<decltype(x)>() << '\n' << rw::get<1>(x); 
} 

Demo (in questo momento funziona solo in clang 3.9): http://melpon.org/wandbox/permlink/9NBqkcbOuURFvypt

Note:

  • Nel demo c'è un'implementazione commentata di nth_type, si può utilizzare per implementare tuple_element direttamente e non rinviare all'implementazione tuple_element<tuple>.
  • get<many> deve essere una funzione membro di many o essere inserito nello spazio dei nomi associato affinché i binding strutturati funzionino. Non dovresti sovraccaricare std::get (sarebbe comunque UB).
  • Ho lasciato l'implementazione di get per i riferimenti non const e i riferimenti di valore r come esercitazione per il lettore.
  • Non c'è alcun esempio di utilizzo di collegamenti e guide strutturati, perché clang non li supporta (in effetti non conosco alcun compilatore che li supporti). In teoria
    template<typename... Types> many(Types...) -> many<Types...>; dovrebbe funzionare.
+0

Buono, ma la domanda era anche come far funzionare questo argomento con argomenti modello dedotti come con il mio esempio di 3 membri. Qualcosa come 'template molti (Args ...) -> molti ;'? –

+0

@JohanLundberg Suppongo che, se il codice originale è valido, sarà sufficiente modificare la "guida" per utilizzare gli argomenti del modello variadic. Ma non ho trovato nessun compilatore online che accetti il ​​tuo codice originale, quindi non ho potuto testare. –

+1

@Revolver_Ocelot: Per fare in modo che funzioni, dovresti fornire 'many' the' tuple_element' e altre interfacce per consentire il funzionamento del binding strutturato. Perché l'associazione strutturata non può accedere alle classi base. Inoltre, non puoi usare la stessa classe base due volte, quindi non puoi avere lo stesso tipo in 'Tipi'. –

2

C'era un discussion about this sulle proposte std proprio l'altro giorno.

Non abbiamo ancora la dicitura finale, o per quello che riguarda un compilatore (di cui sono a conoscenza) che supporta le guide di deduzione, ma secondo Richard Smith la seguente guida alla deduzione dovrebbe funzionare (precis'd):

template<class A, class B> 
struct Agg 
{ 
    A a; 
    B b; 
}; 

template<class A, class B> 
Agg(A a, B b) -> Agg<A, B>; 

Agg agg{1, 2.0}; // deduced to Agg<int, double> 

Quindi sì, anche una guida alla deduzione variadica per un aggregato dovrebbe funzionare e funzionerà con la sintassi di inizializzazione degli aggregati.Non funziona senza una guida alla detrazione, poiché senza una guida alla detrazione il compilatore ha bisogno di un costruttore.

+2

Il problema è che non è possibile avere un tipo i cui membri sono variadici. Cioè, non puoi fare qualcosa come 'Types varname ...;' –