2017-02-28 2 views
2

Ich habe das folgende Snippet Beispielcode:C++: Vorlage Argument Abzug, wenn Template-Funktionen als Argument zu anderen Template-Funktionen vorbei

Die Idee ist, dass es eine Containerklasse, hier Box genannt, und wir möchten Erstellen neuer Versionen dieses Containers durch mapping eine Funktion über seinen aktuellen Inhalt.

#include <iostream> 
#include <tuple> 

template <typename TContent> 
class Box 
{ 
    TContent d_content; 

    public: 

    Box(TContent content) 
    : 
    d_content(content) 
    {} 

    TContent content() const 
    { 
    return d_content; 
    } 

    template <typename Function> 
    auto transform(Function fun) -> decltype(Box{fun(d_content)}) 
    { 
    return Box{fun(d_content)}; 
    }; 

}; 

template <typename TElem> 
std::tuple<TElem, TElem> toTuple(TElem thing) 
{ 
    std::cout << "Transforming " << thing << "to tuple.\n"; 
    return std::make_tuple(thing, thing); 
} 

int main() { 
    std::cout << "Hello World!\n"; 

    Box<int> mybox{10}; 
    Box<int> box2 = mybox.transform([](int content){ return content * 2;}); 
    std::cout << "Transformed box: " << box2.content() << '\n'; 
    Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); // <- Template argument deduction/substitution fails here! 
    std::cout << "Transformed box: " << std::get<0>(box3.content()) << '\n'; 
} 

Try it out here

In Zeile 42, wo box3 erstellt wird, Template-Argument Abzug/Substitution fehlschlägt:

main.cpp: In function 'int main()': 
main.cpp:42:60: error: no matching function for call to 'Box<int>::transform(<unresolved overloaded function type>)' 
    Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); 
                  ^
main.cpp:22:8: note: candidate: template<class Function> decltype (Box<TContent>{fun(((Box<TContent>*)this)->Box<TContent>::d_content)}) Box<TContent>::transform(Function) [with Function = Function; TContent = int] 
    auto transform(Function fun) -> decltype(Box{fun(d_content)}) 
     ^~~~~~~~~ 
main.cpp:22:8: note: template argument deduction/substitution failed: 
main.cpp:42:60: note: couldn't deduce template parameter 'Function' 
    Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); 
                  ^

exit status 1 

Dies scheint der Fall zu sein, wenn Sie versuchen, eine Template-Funktion (Funktion zu übergeben Vorlage?) zu einer Funktion, die selbst einen Template-Argumentparameter erwartet.

Der einzige Weg, dies zu vermeiden, den ich bis jetzt gefunden habe, besteht darin, alle Template-Funktionen in Lambdas oder andere, nicht-Template-Funktionen zu verpacken. Dies ist natürlich suboptimal, da es eine Menge Vortexen einführt.

Warum in diesem Fall versagt, Template-Argument Abzug und ist es eine Möglichkeit, den Code der Box Klasse (und/oder seine transform Elementfunktion) zu verändern, dass Abzug Template-Arguments tut Arbeit zu gewährleisten?

(Der angegebene Code ist C++ 11 als repl.it noch nicht C++ 14 unterstützen. Der Hauptunterschied in C++ 14 wäre, dass der hintere Rückgabetyp transform weggelassen werden kann. Das Fehler, aber bleibt gleich ich bin glücklich, mit Lösungen, die (nur) arbeiten in C++ 14 als auch)

Antwort

2

in Ihrem Beispiel hier:..

template <typename Function> 
auto transform(Function fun) -> decltype(Box{fun(d_content)}) 

Box ist eine Klasse Vorlage, keine Klasse. Innerhalb der Klasse ist Box die injected-class-name, bezieht sich immer speziell auf Box<TContent>. Also, wenn Sie den Typ ändern müssen (wie transform kann sehr wohl tun), wird dies nicht funktionieren. Sie müssen spezifizieren, dieBox Sie auf das, was Function basiert wollen, ist:

template <class Function, 
    class U = std::result_of_t<Function&(TContent)>> 
Box<U> transform(Function fun) 
{ 
    return Box<U>{fun(d_content)}; // or even better, Box<U>{std::invoke(fun, d_content)} 
}; 

Das zweite Problem ist, wenn Sie es nennen:

Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); 

toTuple ist eine Funktion Vorlage, keine Funktion . Sie können es nicht an eine andere Funktionsvorlage übergeben, da es keinen Typ hat, der nicht abgeleitet werden kann.Nach wie vor müssen Sie angeben, welchetoTuple Sie wollen:

Box<std::tuple<int, int>> box3 = mybox.transform(toTuple<int>); 

Oder das Ganze in einem Lambda-Wrap (dies ist eine vereinfachte Implementierung, die nicht über Kopien, Referenzen schert oder SFINAE):

Box<std::tuple<int, int>> box3 = mybox.transform([](auto x) { return toTuple(x); }); 
+0

Vielen Dank. Aus irgendeinem Grund kompilieren C++ 11 und C++ 14 glücklich den illformed Dekltyp. Unabhängig davon, es in 'Box ' würde es richtig machen, oder? – Qqwy

+0

Wie "toTuple" ist eine Funktionsvorlage, keine Funktion. Sie können es nicht an eine andere Funktionsvorlage übergeben, da es keinen Typ hat, es kann nicht abgeleitet werden. " -> Gibt es Fälle, in denen eine Funktionsvorlage an eine andere Funktionsvorlage übergeben wird, aus der sie abgeleitet werden könnte? – Qqwy

+1

@Qqwy Ja, könnte man. Und nein, es gibt keine. – Barry

Verwandte Themen