2015-03-21 11 views
6

Ho il seguente codice.deduci il tipo di elementi di tupla in C++ 11

template <typename... Types> 
void print_tuple(const std::tuple<Types&&...>& value) 
{ 
    std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl; 
} 
print_tuple(std::forward_as_tuple("test",1)); 
quale compilatore

lamenta

error: invalid initialization of reference of type ‘const std::tuple<const char (&&)[5], int&&>&’ from expression of type ‘std::tuple<const char (&)[5], int&&>’ 
    print_tuple(std::forward_as_tuple("test",1)); 

Perché compilatore dedurre il tipo del primo elemento nella tupla di essere const char (& &) [5]?

+0

Non è nemmeno necessario catturare una tupla in modo specifico. Perché non semplicemente 'modello void print_tuple (Tuple && value)'? – 0x499602D2

+0

Dato che non era chiaro ad almeno due persone: la tua definizione 'print_tuple' * fa * consente la chiamata se gli argomenti del template sono esplicitamente specificati come 'print_tuple '. È una buona domanda perché il compilatore deduca i primi 'Tipi 'in modo diverso. – hvd

+0

@hvd Potrebbe essere perché una stringa letterale è un lvalue e viene inoltrata come tale. – 0x499602D2

risposta

2

In generale, per la deduzione per il successo, l'argomento deve avere la stessa forma generale del parametro. Esistono alcune eccezioni in cui è possibile ricavare(selezionando T = U &), ma in questo caso non è stata specificata alcuna eccezione.

14.8.2.5, dedurre argomenti del template da un tipo [temp.deduct.type]

8 Un tipo di modello argomento T, un argomento template TT o un argomento di template non i tipo possono essere dedotto se P e A avere una delle seguenti forme:

[...]

T&
T&&

[...]

Non è esattamente chiaro, ma questo richiede P (parametro) e A (l'argomento) per entrambi hanno la stessa forma. Devono essere entrambi del modulo T& o entrambi del modulo T&&.Le eccezioni, le circostanze in cui T && desumibile dalla U &, sono fatte modificando T && al normale T prima l'abbinamento avviene, in circostanze limitate:

10 Allo stesso modo, se P ha una forma che contiene (T), allora ogni tipo di parametro Pi del rispettivo parametro -type-list di P viene confrontato con il corrispondente tipo di parametro Ai del corrispondente parametro di tipo-list di A. Se P e A sono tipi di funzione originati dalla deduzione quando si prende l'indirizzo di un modello di funzione (14.8.2.2) o quando si deducono gli argomenti del modello da una dichiarazione di funzione (14.8.2.6) e Pi e Ai sono parametri di livello superiore parametro di tipo-list di P e A, rispettivamente, Pi viene regolata se è un riferimento rvalue ad un parametro di modello cv-qualificato ed Ai è un riferimento assegnabile, nel qual caso il tipo di Pi è cambiato per essere il modello tipo di parametro (ad esempio, T&& viene modificato in T). [...]

e

14.8.2.1 Dedurre argomenti di template da una chiamata di funzione [temp.deduct.call]

3 [...] Se P è un riferimento di rvalore a un parametro di modello cv-non qualificato e l'argomento è un lvalue, il tipo "riferimento di lvalue a A" viene utilizzato al posto di A per la deduzione di tipo. [...]

ma nessuna eccezione simile si applica al tuo scenario.

E 'lo stesso principio che rende

template <typename T> struct S { }; 
template <typename T> void f(S<const T>) { } 
int main() { f(S<void()>()); } 

non valida: const T non può essere dedotto dal void(), anche se T = void() darebbe esattamente questo risultato, e chiamando f<void()> sarebbe successo.

risposta cancellato di Invernomuto ha dimostrato che è possibile utilizzare

template <typename... Types>  // vv-- change here 
void print_tuple(const std::tuple<Types...>& value) 

invece: questo permette Types a dedurre come riferimenti lvalue, come riferimenti rvalue, o come non-riferimenti, a seconda del tipo di value.

0

Hai intenzione di utilizzare && in std::tuple<Types&&...> come riferimento universale? Questo non è un riferimento universale; è un riferimento di rvalue e può essere associato solo ai rvalues. Si può fare in questo modo per controllare che tipo di riferimento è:

template<typename T> 
class TD; 

quindi definire la funzione del modello:

template <typename... Types> 
void print_tuple(const std::tuple<Types&&...>& value) 
{ 
    TD<decltype(value)> a; 
    std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl; 
} 

Poi si vedrà l'errore di compilazione come:

implicit instantiation of undefined template 'TD<const std::__1::tuple<std::__1::basic_string<char> &&, int &&> &>' 

Puoi vedere che anche per il tipo int, si deduce che sia un riferimento di valore. I riferimenti Rvalue non possono essere associati a lvalue. Si può provare che chiamando:

int i = 1; 
print_tuple(std::forward_as_tuple(i,1)); // error. 

Pertanto, è giusto che const char(&&)[5] si deduce, e una stringa letterale non può essere convertito in const char(&&)[5]. Se si chiama print_tuple come:

print_tuple(std::forward_as_tuple(string("test"),1)); 

Funzionerà. Ora il tipo è tuple<string&&, int&&>.

+0

Come ho commentato la domanda, se esplicitamente spiegando gli argomenti del template, come in 'print_tuple ', funziona. Questa risposta è sbagliata. 'T &&' non è necessariamente un riferimento di valore. Se "T" è un riferimento di lvalue, allora "T &&" è solo "T". L'unico problema è che la deduzione non funziona: quando si suppone che 'T &&' sia' U &', 'T' non viene dedotto come' U & ', anche se potrebbe essere. – hvd

+0

@hvd Non c'è niente nella lingua che dice che 'T' potrebbe essere dedotto come' U & 'in questo contesto. Le condizioni per [temp.deduct.call]/3 non sono soddisfatte. Un esempio più semplice è 'template void f (const T &&)' che non si legherà a lvalue. A meno che, ovviamente, non si specializzi esplicitamente il modello, che non è realmente rilevante in una domanda sulla deduzione degli argomenti del modello. – Oktalist

+0

@Oktalist Non richiede la specializzazione del modello. Richiede semplicemente di specificare gli argomenti del modello. È possibile chiamare 'f ' con un'espressione di 'int' lvalue senza problemi, anche se la deduzione non ti darà mai' T = int &'. Ho appena postato una risposta con qualche altro dettaglio. – hvd

Problemi correlati