2012-09-03 7 views
6

Ich versuche eine Template-Funktion zu erstellen, die eine iterable und eine Funktion akzeptiert, so dass die übergebene Funktion implizit an eine std::function übergeben wird des entsprechenden Typs (so dass es sowohl mit vollen Funktionen als auch mit lambdas verwendet werden kann).(g ++ 4.7.1) Ersetzen von expliziten Typnamen durch eine äquivalente Klasse typedef kann nicht kompiliert werden

Hier ist der Code:

#include <iostream> 
#include <vector> 
#include <algorithm> 
#include <typeinfo> 


template<typename T> 
void bar(const T & base, std::function<bool(int)> f) // works 
//void bar(const T & base, std::function<bool(typename T::iterator::value_type)> f) // fails to compile 
{ 
    std::cout << ((typeid(std::function<bool(int)>) == typeid(std::function<bool(typename T::iterator::value_type)>))?"identical":"distinct") << std::endl; 
} 

bool filter(int x) { return x%2==0; } 

int main() { bar(std::vector<int> {0, 1}, filter); } 

Zusammengestellt mit g++-4.7 -std=c++11 -o itest itest.cpp dies erzeugt identical.

Wenn Sie uncomment Linie 10 und Kommentarzeile 9 und wie oben kompilieren, schlägt stattdessen Kompilation mit

g++-4.7 -std=c++11 -Wall -Werror -o itest itest.cpp 
itest.cpp: In function 'int main()': 
itest.cpp:16:53: error: no matching function for call to 'bar(std::vector<int>, bool (&)(int))' 
itest.cpp:16:53: note: candidate is: 
itest.cpp:9:10: note: template<class T> void bar(const T&, std::function<bool(typename T::iterator::value_type)>) 
itest.cpp:9:10: note: template argument deduction/substitution failed: 
itest.cpp:16:53: note: mismatched types 'std::function<bool(typename T::iterator::value_type)>' and 'bool (*)(int)' 

Ich sollte anmerken, dass die unveränderte Version mit Xcode erfolgreich ist (die entsprechenden Optionen festgelegt haben), aber ich d bleibe lieber mit g ++ über Klängen wenn möglich. Mache ich etwas falsch oder ist das ein bekannter Fehler in g ++?

Antwort

5

Entschuldigung, aber der Fehler ist in Ihrem Code. Es ist äquivalent zu:

template<typename T> struct S { template<typename U> S(const U &); }; 
template<typename T> void bar(T, S<T>); 
int main() { bar(5, 6); } 

Das Problem ist, dass in Vorlage Argument Abzug/Substitution, wenn ein Template-Argument erscheint (entweder direkt oder in eine abhängige Art der Konstruktion) in mehr als ein Argumente dann beiden Argumente genau übereinstimmen müssen; Benutzerdefinierte Konvertierungen werden nicht berücksichtigt, auch wenn aus einem Argument ersichtlich ist, um welchen Typ es sich handelt.

Die benutzerdefinierte Konvertierung ist hier der implizite Konstruktor von .

Mögliche fix wäre bar ausdrücklich (wie bar<int>) zu instanziiert, oder an eine Hilfsfunktion zu versenden:

template<typename T> 
void bar_impl(const T & base, std::function<bool(typename T::iterator::value_type)> f) 
{ 
    std::cout << ((typeid(std::function<bool(int)>) == typeid(std::function<bool(typename T::iterator::value_type)>))?"identical":"distinct") << std::endl; 
} 

template<typename T, typename F> 
void bar(const T & base, F &&f) 
{ 
    bar_impl<T>(base, std::forward<F>(f)); 
} 
+0

Huh, danke. Ich habe eine andere (ziemlich schreckliche) Problemumgehung implementiert, die nicht erfordert, dass der Aufrufer sich jemals auf den zweiten Typ bezieht oder diesen ableitet. – Bakkot

+0

Hoppla, vergiss die [link] (https://gist.github.com/a172651b93dc4670f295). Für Leute, die nicht beide lesen wollen, ist es im Wesentlichen currying. – Bakkot

2

Sie benötigen zweite Überlastung für Zeigerfunktion - dann wird es kompilieren. Implizite cast std::function nicht funktionieren:

void bar(const T & base, bool(*f)(typename T::value_type)){ 
    std::cout << "ptr func\n"; 
} 

Umgehen für Problem durch ecatmur beschrieben (mehrere T, mit nicht-Typen in Funktion signutre passend): Sie können andere T wickeln in identity Struktur, die wie so definiert:

template<class T> struct identity{ typedef T type; }; 

dann ignoriert der Compiler diese T für den Typabzug.

Verwandte Themen