2015-12-09 26 views
15

Intendo perché esiste std::make_tuple? So che ci sono situazioni in cui la funzione riduce la quantità di caratteri che devi digitare perché puoi evitare i parametri del modello. Ma è l'unica ragione? Che cosa rende speciale la funzione std::tuple mentre altri modelli di classe non hanno tale funzione? È solo perché è possibile utilizzare std::tuple più spesso in tali situazioni?Qual è la ragione per `std :: make_tuple`?


Ecco due esempi in cui std::make_tuple riduce la quantità di personaggi:

// Avoiding template parameters in definition of variable. 
// Consider that template parameters can be very long sometimes. 
std::tuple<int, double> t(0, 0.0); // without std::make_tuple 
auto t = std::make_tuple(0, 0.0); // with std::make_tuple 

// Avoiding template parameters at construction. 
f(std::tuple<int, double>(0, 0.0)); // without std::make_tuple 
f(std::make_tuple(0, 0.0));   // with std::make_tuple 

Ma, come scritto sopra, non si dispone di una funzione come questa per molti altri modelli di classe.

+3

Esistono varie funzioni simili, ma il confronto diretto più ovvio è ['std :: make_pair'] (http://en.cppreference.com/w/cpp/utility/pair/make_pair). Penso che una regola empirica potrebbe essere che esistono se si volesse costruire questi oggetti semplicemente come parte di un'altra espressione, per chiamare una funzione, ecc. – BoBTFish

+0

Per quali altre classi ti piacerebbe avere una funzione 'make_ *'? Stiamo già ottenendo make_shared, make_unique, ecc. –

+1

L'unica differenza è la deduzione di tipo. Ma la deduzione di tipo può essere utile, ecco perché. –

risposta

18

Perché non è possibile utilizzare la deduzione argomento per i costruttori. È necessario scrivere esplicitamente std::tuple<int, double>(i,d);.

Rende più conveniente creare una tupla e passarla a un'altra funzione in one-shot.

takes_tuple(make_tuple(i,d)) vs takes_tuple(tuple<int,double>(i,d)).

Un posto in meno da modificare quando cambia il tipo di i o d, soprattutto se ci sono possibili conversioni tra il vecchio e il nuovo tipo.

se fosse possibile scrivere std::tuple(i,d);, make_* sarebbe (probabilmente) essere ridondante.

(Non chiedere perché qui Forse per ragioni analoghe per cui sintassi A a(); non invoca un costruttore di default Ci sono alcuni dolorosi C++ peculiarità di sintassi...)

UPDATE NOTA: Come Daniel giustamente le avvertenze, C++ 17 saranno migliorate, così che la deduzione degli argomenti template funzionerà per i costruttori, e tale delega diventerà obsoleta.

+4

Dal ** C++ 17 **, sarà possibile utilizzare ** la deduzione del modello di classe **, cioè, per scrivere 'std :: tuple t (1, 1.0, 'a');' o 'auto t = std :: tuple (1, 1.0, 'a'); '. Vedi, ad esempio, [qui] (http://en.cppreference.com/w/cpp/language/class_template_deduction) per i dettagli. –

+0

Include la nota di aggiornamento nella risposta. Grazie per averlo chiamato. – luk32

+0

@ luk32 "e tale delega diventerà obsoleta" Non sono d'accordo: 'std :: make_tuple' userà' X & 'se avvolgi qualcosa nella chiamata' std :: ref() ', che sarà comunque utile in C++ 17 , come [le guide alla deduzione per 'std :: tuple'] (http://en.cppreference.com/w/cpp/utility/tuple/deduction_guides) non sembrano gestire questo caso. –

7

Siamo in grado di trovare una spiegazione razionale per questo che abbiamo bisogno make_tuple e le varie altre utilità make_ * a proposta N3602: Template parameter deduction for constructors che dice (sottolineatura mia):

Questo documento propone di estendere parametro di modello detrazione per le funzioni per i costruttori di classi template. Il modo più chiaro per descrivere il problema e la soluzione è con alcuni esempi.

Supponiamo di aver definito quanto segue.

vector<int> vi1 = { 0, 1, 1, 2, 3, 5, 8 }; 
vector<int> vi2; template<class Func> 
    class Foo() { 
     public: Foo(Func f) : func(f) {} 
     void operator()(int i) { os << "Calling with " << i << endl; f(i); } 
     private: 
     Func func; 
    }; 

Attualmente, se vogliamo istanziare classi template, abbiamo bisogno di specificare i parametri del modello o utilizzare una "make_ *" wrapper, leva parametro di template deduzione per le funzioni, o punt completamente:

pair<int, double> p(2, 4.5); 
auto t = make_tuple(4, 3, 2.5); 
copy_n(vi1, 3, back_inserter(vi2)); // Virtually impossible to pass a lambda to a template class' constructor 
for_each(vi.begin(), vi.end(), Foo<???>([&](int i) { ...})); 

Nota, la proposta viene monitorata tramite EWG issue 60.

+0

@black bene il problema EWG che ho appena aggiunto alla mia risposta suggerisce alcuni problemi, ma il lavoro è stato incoraggiato. L'elenco attivo dell'EWG non è stato ancora aggiornato da Kona, quindi forse questo è cambiato. –

1

Solo per deduzione argomento modello. Tuttavia, ecco un (forzato) esempio in cui ciò sia necessario per l'utilizzo di un lambda:

class A 
{ 
public: 
    template<typename F> 
    A(const std::tuple<F> &t) 
    { 
     // e.g. 
     std::get<0>(t)(); 
    } 
}; 

class B : public A 
{ 
public: 
    B(int i) : A(std::make_tuple([&i]{ ++i; })) 
    { 
     // Do something with i 
    } 
}; 

std::tuple<decltype([&i]{ ++i; })>([&i]{ ++i; }) non può essere utilizzato perché le due espressioni lambda hanno tipi differenti. Un wrapper polimorfico come std::function aggiunge un sovraccarico di runtime. Una classe denominata con operator() definita dall'utente funzionerebbe (che potrebbe anche essere un amico di B, a seconda del contenuto del corpo dell'operatore). Questo è quello che usavamo nei vecchi tempi prima del C++ 11.