2010-04-19 10 views
23

To prawdopodobnie będzie bardzo proste wyjaśnienie, ale zamierzam dać jak najwięcej historii, na wypadek, gdy się mylę. Zaawansowane przeprosiny za bycie tak gadatliwym. Używam gcc4.5 i zdaję sobie sprawę, że obsługa C++ 0x jest nadal nieco eksperymentalna, ale zamierzam działać z założeniem, że istnieje przyczyna niezwiązana z błędem dla obserwowanego zachowania.Dziwne zachowanie podczas rekurencyjnego budowania typu zwrotu dla funkcji variadycznych

Eksperymentuję z szablonami funkcji variadic. Ostatecznym celem było zbudowanie listy cons z listy std::pair. Nie był to typ niestandardowy, tylko ciąg obiektów par. Funkcja, która tworzy listę, musiałaby być w pewien sposób rekursywna, a ostateczna wartość zwracana jest zależna od wyniku wywołań rekursywnych. Jako dodatkowy zwrot, kolejne parametry są dodawane razem przed wstawieniem do listy. Więc jeśli przejdę [1, 2, 3, 4, 5, 6], końcowy wynik powinien wynosić {1 + 2, {3 + 4, 5 + 6}}.

Moja pierwsza próba była dość naiwna. Funkcja, Build, z dwoma przeciążeniami. Jeden wziął dwa identyczne parametry i po prostu zwrócił ich sumę. Drugi wziął dwa parametry i pakiet parametrów. Wartością zwracaną była para składająca się z sumy dwóch ustawionych parametrów i rekurencyjnego wywołania. Z perspektywy czasu była to oczywiście wadliwa strategia, ponieważ funkcja nie jest zadeklarowana, gdy próbuję określić jej typ zwracany, więc nie ma innego wyjścia, jak tylko rozwiązać wersję nierekurencyjną.

To rozumiem. Gdzie się pomyliłem, była druga iteracja. Postanowiłem utworzyć te funkcje statycznymi członkami klasy szablonów. Same wywołania funkcji nie są sparametryzowane, ale zamiast tego cała klasa jest. Zakładam, że gdy funkcja rekursywna podejmie próbę wygenerowania swojego typu powrotu, utworzy nową wersję struktury z własną funkcją statyczną i wszystko będzie działać.

Wynik był następujący: „Błąd: nie pasującej funkcji dla wywołania do BuildStruct<double, double, char, char>::Go(const char&, const char&)

kodu naruszającym przepisy:

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

My zamieszanie wynika z faktu, że parametry do BuildStruct powinna być zawsze taka sama typy jako argumenty wysłane do BuildStruct::Go, ale w kodzie błędu Go brakuje początkowych dwóch podwójnych parametrów. Czego tu mi brakuje? Gdyby moje początkowe założenie dotyczące wyboru funkcji statycznych było nieprawidłowe, dlaczego próbuje wywołać niewłaściwą funkcję, a nie tylko nie znaleźć funkcji? Wygląda na to, że po prostu mieszamy typy chcąc nie chcąc, a ja po prostu nie mogę wymyślić wyjaśnienia, dlaczego. Jeśli dodaję dodatkowe parametry do początkowego wywołania, zawsze ukrywa się do tego ostatniego kroku, zanim ulegnie awarii, więc przypuszczalnie sama rekursja działa przynajmniej częściowo. Jest to sprzeczne z początkową próbą, która zawsze nie odnalazła wywołania funkcji od razu.

Ostatecznie, przeszedłem problem, z dość eleganckim rozwiązaniem, które prawie nie przypomina żadnej z dwóch pierwszych prób. Więc wiem, jak robić to, co chcę robić. Szukam wyjaśnienia porażki, którą widziałem.

Pełny kod do naśladowania, ponieważ jestem pewien, że mój słowny opis był niewystarczający. Najpierw jakiś wzorzec, jeśli czujesz się zmuszony do wykonania kodu i sam go zobaczysz. Następnie pierwsza próba, która nie powiodła się rozsądnie, to druga próba, która się nie udała.

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

rozwiązanie dla struktura:

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

powstałych błędów:

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 rozwiązanie:

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

powstałych błędów:

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

Nie można tego teraz wypróbować, ale może się zdarzyć, że odroczenie odroczonego zwrotu nadal się nie udaje. W drugim przypadku powinieneś móc wpisać typ zwracany: 'typedef std :: pair :: result_type> result_type;' (i odpowiednio dla pierwszej specjalizacji) i użyj tych: 'static result_type Go (...); ' – visitor

+0

Myślę, że próbowałem, ale nie pamiętam wyniku. Zrobię jeszcze raz, kiedy wrócę do domu. Dzięki. –

+31

Dobry panie, to * dużo * do przeczytania :) –

Odpowiedz

2

Czytanie komentarzy, wydaje się wystarczająco jasne, że jest to bardzo zlokalizowany błąd w konkretnej wersji G ++, i to jest odpowiedź, jaka kiedykolwiek będzie.

+0

Doszedłem do tego wniosku miesiąc temu, dziękuję za odpowiedź, dzięki czemu mogę odejść :) –