2016-10-17 3 views
2

Ich habe das folgende Problem. Wenn ich versuche, den folgenden Code zu kompilierenTyp Abzug funktioniert nicht

template< typename T > 
T func(T t) 
{ 
    return t; 
} 

template< size_t N, typename T > 
void foo(std::function< T(T) > func) 
{ 
    // ... 
} 

int main() 
{ 
    foo<3>(func<float>); 

    return 0; 
} 

ich den Fehler:

no matching function for call to 'foo' 
     foo<3>(func<float>); 
     ^~~~~~ 
/Users/arirasch/WWU/dev/xcode/tests/tests/main.cpp:18:10: note: candidate template ignored: could not match 'function<type-parameter-0-1 (type-parameter-0-1)>' against 'float (*)(float)' 
    void foo(std::function< T(T) > func) 

Allerdings, wenn ich es zu

beheben
template< typename T > 
T func(T t) 
{ 
    return t; 
} 

template< size_t N, typename T > 
void foo(std::function< T(T) > func) 
{ 
    // ... 
} 

int main() 
{ 

    std::function< float(float) > input_func = func<float>; 
    foo<3>(input_func); 

    return 0; 
} 

das heißt, wenn ich erkläre die Eingabefunktion von foo explizit wie std::function< float(float) >, kann die Kompilierung erfolgreich durchgeführt werden.

Wer weiß, wie ich meinen Code alternativ fixe kann, so dass ich einfach so etwas wie foo<3>(func<float>); (nach meinem ersten Codebeispiel) anstelle von

std::function< float(float) > input_func = func<float>; 
foo<3>(input_func); 

wo die Art der input_func muss explizit angegeben werden schreiben kann?

Vielen Dank im Voraus.

+1

Funktionen sind nicht 'std :: function's. –

Antwort

3

Typabzug funktioniert in Ihrem Fall nicht einfach, weil es nicht abgeleitet werden kann. Typabzug ist in den meisten Fällen eine einfache Übereinstimmung mit Typen und anderen Vorlagenparametern. Es gibt jedoch eine dunkle Ecke von C++, die sich mit Deduktion befassen, die einige funky Regeln hat, aber ich werde für diese Antwort nicht darauf eingehen.

Dies ist ein Beispiel, wo der Compiler Template Argumente ableiten kann:

template<typename T> 
void test(std::vector<T>); 

test(std::vector<int>{1, 2, 3, 4, 5, 6}); 

Dies ist für den Compiler einfach. Es braucht einen Vektor von T. Sie geben ihm einen Vektor von ganzen Zahlen. T muss int sein.

jedoch in Ihrem Fall gibt es viel mehr Sachen passiert:

template<typename T> 
void test(std::function<T(T)>); 

int someFunc(int); 

test(someFunc); 

Der Compiler das Spiel nicht tun kann. Probieren Sie es selbst aus: Geben Sie mir eine T, die diese beiden Typen gleich macht: int(*)(int) bis std::function<T(T)>. In der Tat gibt es keine T, die diese beiden Typen gleich machen kann, während die Vektorversion eine einfache Übereinstimmung war.

Sie werden mir sagen: "aber ... ein Zeiger auf eine Funktion ist in eine std :: -Funktion umwandelbar, du alberne!" Ja, es ist tatsächlich umwandelbar. Aber vor jeder Konvertierung muss der Compiler finden, was T ist. Ohne T, machen Sie die Umwandlung von einem Zeiger zur Funktion in welche Klasse? Viele Klassen? Versuchen Sie, alle T übereinstimmen? Es gibt mehrere Möglichkeiten, wo Ihre Funktion konvertierbar wäre.


Wie können Sie diese Arbeit machen? Vergiss die std::function. Erhalten Sie einfach T.

template<typename T> 
T func(T t) { 
    return t; 
} 

template<size_t N, typename T> 
void foo(T func) { 
    // ... 
} 

int main() 
{ 
    foo<3>(func<float>); 

    return 0; 
} 

Beachten Sie, wie dieses Beispiel gut funktioniert. Sie haben keine Konvertierung, keine std::function Dings und können mit allen aufrufbaren Dateien arbeiten, die Sie sich vorstellen können!

Sind Sie besorgt, irgendeinen Typ anzunehmen? Keine Sorge hier! Parametertypen sind eine schlechte Methode, um auszudrücken, was eine Vorlage ohnehin mit empfangenen Parametern tun kann. Sie sollten es mit einem Ausdruck einschränken. Dieser Ausdruck wird anderen sagen, wie Sie T verwenden und welche Schnittstelle T haben müssen. btw, nennen wir diese sfinae:

template<size_t N, typename T> 
auto foo(T func) -> void_t<decltype(func(std::declval<int>()))> { 
    // ... 
} 

In diesem Beispiel beschränken Sie func mit einem int aufrufbar zu sein.

Wenn Sie nicht über ein C++ 17-Compiler noch haben, können Sie void_t wie folgt implementieren:

template<typename...> 
using void_t = void; 
+0

"Typ Abzug ist ein einfaches Spiel ..." Nun, nein. Werfen Sie einen Blick auf die Liste der Orte, an denen es auftritt, und welche abgeleiteten Kontexte existieren. Es ist ein Beweis für die C++ - Sprache, dass Sie _think_ es ist jedoch einfach. – MSalters

+0

Wahr. Ich werde meine Antwort bearbeiten. Danke für das Aufzeigen. –

4

Das Problem hier ist, dass der Compiler Überladungsauflösung durchführen muss, um herauszufinden, welche std::function<U(U)> Instanziierungen Konstruktoren haben, die eine T(*)(T) nehmen können. Das heißt, es könnte mehrere Typen geben, von denen jeder mehrere Schlüssel haben könnte, die Ihre input_func übernehmen könnten.

Nun, wenn Sie auf der Standard-anschauen, werden Sie feststellen, dass es keine solche Überlastung für std::function angegeben ist, aber die Überladungsauflösung Regeln sind die gleichen für alle Vorlagen, ob sie von std::, boost:: oder ACME::. Der Compiler startet nicht die Instanziierung von Vorlagen, um eine Konvertierungssequenz zu finden.

Sobald Sie eine perfekte Übereinstimmung bereitstellen, ist keine Konvertierungssequenz erforderlich. Es gibt genau einen Zieltyp, für den keine Konvertierungssequenz benötigt wird, und der Compiler leitet diesen Typ ab.

In diesem speziellen Fall wissen Sie über die Beziehung zwischen Funktionszeigern und std::function, und auch über die spezifische Einschränkung, dass Sie müssen eine einheitliche Funktion haben, die die gleiche Art gibt (keine T(*)(U)), so dass Sie könnte eine Überlastung

hinzufügen
2

Casting-Funktion zu einer std :: -Funktion löst explizit das Problem. Z.B. Der folgende Code funktioniert.

template< typename T > 
T func(T t) 
{ 
    return t; 
} 

template<typename T> 
using FuncType = std::function<T(T)>; 

template< size_t N, typename T > 
void foo(FuncType<T> func) 
{ 
    // ... 
} 

int main() 
{ 
    func<float>(1.0); 
    foo<3>(FuncType<float>(func<float>)); 

    return 0; 
}