2010-04-19 14 views
23

Questa sarà probabilmente una spiegazione molto semplice, ma fornirò il maggior numero possibile di retroscena nel caso in cui io abbia torto. Scuse avanzate per essere così prolisso. Sto usando gcc4.5 e mi rendo conto che il supporto per C++ 0x è ancora un po 'sperimentale, ma ho intenzione di agire sul presupposto che ci sia un motivo non correlato al bug per il comportamento che sto vedendo.Comportamento anomalo quando si costruisce in modo ricorsivo un tipo di ritorno per le funzioni variad.

Sto sperimentando modelli di funzioni variadiche. L'obiettivo finale era quello di creare una lista di cose da std::pair. Non era pensato per essere un tipo personalizzato, solo una stringa di oggetti coppia. La funzione che costruisce la lista dovrebbe essere in qualche modo ricorsiva, con il valore di ritorno finale che dipende dal risultato delle chiamate ricorsive. Come ulteriore aggiunta, i successivi parametri vengono sommati prima di essere inseriti nell'elenco. Quindi se passo [1, 2, 3, 4, 5, 6] il risultato finale dovrebbe essere {1 + 2, {3 + 4, 5 + 6}}.

Il mio tentativo iniziale era abbastanza ingenuo. Una funzione, Build, con due overload. Uno ha preso due parametri identici e ha semplicemente restituito la somma. L'altro ha preso due parametri e un pacchetto di parametri. Il valore di ritorno era una coppia composta dalla somma dei due parametri impostati e dalla chiamata ricorsiva. In retrospettiva, questa era ovviamente una strategia imperfetta, perché la funzione non è dichiarata quando provo a capire il suo tipo di ritorno, quindi non ha altra scelta che risolverla nella versione non ricorsiva.

Che io capisca. Dove mi sono confuso è stata la seconda iterazione. Ho deciso di rendere quelle funzioni membri statici di una classe template. Le chiamate di funzione non sono parametrizzate, ma è invece l'intera classe. La mia ipotesi era che quando la funzione ricorsiva tenta di generare il suo tipo di ritorno, istanziare una versione completamente nuova della struttura con la propria funzione statica, e tutto dovrebbe funzionare da solo.

Il risultato è stato: "Errore: nessuna funzione di corrispondenza per la chiamata al BuildStruct<double, double, char, char>::Go(const char&, const char&)"

Il codice incriminato:

static auto Go(const Type& t0, const Type& t1, const Types&... rest) 
    -> std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))> 

La mia confusione deriva dal fatto che i parametri per BuildStruct devono essere sempre lo stesso digita gli argomenti inviati a BuildStruct::Go, ma nel codice di errore Go mancano i due parametri iniziali iniziali. Cosa mi manca qui? Se la mia ipotesi iniziale su come sarebbero state scelte le funzioni statiche era errata, perché sta cercando di chiamare la funzione sbagliata piuttosto che non semplicemente trovare una funzione? Sembra che stia semplicemente mescolando i tipi, volenti o nolenti, e non riesco a trovare una spiegazione del perché. Se aggiungo parametri aggiuntivi alla chiamata iniziale, esso scava sempre fino all'ultimo passaggio prima di fallire, quindi presumibilmente la ricorsione stessa è almeno parzialmente funzionante. Questo è in diretto contrasto con il tentativo iniziale, che non è mai riuscito a trovare subito una chiamata di funzione.

In definitiva, ho superato il problema, con una soluzione abbastanza elegante che difficilmente assomiglia a uno dei primi due tentativi. Quindi so come fare ciò che voglio fare. Sto cercando una spiegazione per il fallimento che ho visto.

Codice completo da seguire poiché sono sicuro che la mia descrizione verbale era insufficiente. Prima un po 'di piastra, se ti senti in dovere di eseguire il codice e vederlo da solo. Quindi il tentativo iniziale, che fallì ragionevolmente, poi il secondo tentativo, che non lo fece.

#include <iostream> 
using std::cout; 
using std::endl; 

#include <utility> 

template<typename T1, typename T2> 
std::ostream& operator <<(std::ostream& str, const std::pair<T1, T2>& p) { 
    return str << "[" << p.first << ", " << p.second << "]"; 
} 

//Insert code here  

int main() { 
    Execute(5, 6, 4.3, 2.2, 'c', 'd'); 
    Execute(5, 6, 4.3, 2.2); 
    Execute(5, 6); 

    return 0; 
} 

Soluzione non struct:

template<typename Type> 
Type BuildFunction(const Type& t0, const Type& t1) { 
    return t0 + t1; 
} 

template<typename Type, typename... Rest> 
auto BuildFunction(const Type& t0, const Type& t1, const Rest&... rest) 
     -> std::pair<Type, decltype(BuildFunction(rest...))> { 
    return std::pair<Type, decltype(BuildFunction(rest...))> 
        (t0 + t1, BuildFunction(rest...)); 
} 

template<typename... Types> 
void Execute(const Types&... t) { 
    cout << BuildFunction(t...) << endl; 
} 

risultanti errori:

soluzione
test.cpp: In function 'void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]': 
test.cpp:33:35: instantiated from here 
test.cpp:28:3: error: no matching function for call to 'BuildFunction(const int&, const int&, const double&, const double&, const char&, const char&)' 

Struct:

template<typename... Types> 
struct BuildStruct; 

template<typename Type> 
struct BuildStruct<Type, Type> { 
    static Type Go(const Type& t0, const Type& t1) { return t0 + t1; } 
}; 

template<typename Type, typename... Types> 
struct BuildStruct<Type, Type, Types...> { 
    static auto Go(const Type& t0, const Type& t1, const Types&... rest) 
     -> std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))> { 
    return std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))> 
       (t0 + t1, BuildStruct<Types...>::Go(rest...)); 
    } 
}; 

template<typename... Types> 
void Execute(const Types&... t) { 
    cout << BuildStruct<Types...>::Go(t...) << endl; 
} 

risultanti errori:

test.cpp: In instantiation of 'BuildStruct<int, int, double, double, char, char>': 
test.cpp:33:3: instantiated from 'void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]' 
test.cpp:38:41: instantiated from here 
test.cpp:24:15: error: no matching function for call to 'BuildStruct<double, double, char, char>::Go(const char&, const char&)' 
test.cpp:24:15: note: candidate is: static std::pair<Type, decltype (BuildStruct<Types ...>::Go(BuildStruct<Type, Type, Types ...>::Go::rest ...))> BuildStruct<Type, Type, Types ...>::Go(const Type&, const Type&, const Types& ...) [with Type = double, Types = {char, char}, decltype (BuildStruct<Types ...>::Go(BuildStruct<Type, Type, Types ...>::Go::rest ...)) = char] 
test.cpp: In function 'void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]': 
test.cpp:38:41: instantiated from here 
test.cpp:33:3: error: 'Go' is not a member of 'BuildStruct<int, int, double, double, char, char>' 
+0

Impossibile provarlo in questo momento, ma potrebbe essere che la deduzione del tipo di reso posticipato non sia ancora riuscita. Nel secondo caso dovresti essere in grado di digitare il tipo restituito: 'typedef std :: pair :: result_type> result_type;' (e opportunamente per la prima specializzazione) e usa quelli: 'static result_type Go (...); ' – visitor

+0

Penso di averlo provato, ma non ricordo il risultato. Darò un altro colpo quando torno a casa. Grazie. –

+31

Buon Dio, è * molto * da leggere :) –

risposta

2

Leggendo i commenti, sembra abbastanza chiaro che si tratta di un bug molto localizzato in una particolare versione di G ++, e questa è tutta la risposta che ci sarà mai.

+0

Ho raggiunto questa conclusione un mese fa, grazie per aver risposto così posso farla andare via :) –

Problemi correlati