2017-02-28 5 views
2

je l'extrait suivant d'un exemple de code:C++: déduction argument du modèle lors du passage des fonctions de modèle comme argument à d'autres fonctions de modèle

L'idée est qu'il ya une classe de conteneur, appelé ici Box, et que nous pourrions vouloir pour créer de nouvelles versions de ce conteneur par mapping une fonction sur son contenu actuel.

#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

à la ligne 42, où box3 est créé, déduction argument template/substitution échoue:

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 

Cela semble être le cas lors d'une tentative de passer d'une fonction de modèle (fonction template?) à une fonction qui attend elle-même un paramètre d'argument template. La seule façon d'éviter cela que j'ai trouvé jusqu'à présent est d'envelopper toutes les fonctions de modèle dans lambdas ou d'autres fonctions, non-modèle. Ceci est bien sûr sous-optimal, car il introduit beaucoup de passe-partout.

Pourquoi déduction argument modèle parviennent pas dans ce cas, et est-il un moyen de modifier le code de la classe Box (et/ou sa fonction de membre transform) pour faire en sorte que la déduction des arguments de modèle fait travail?

(Le code donné est 11 C++ comme repl.it ne supporte pas encore C++ 14. La principale différence en C++ 14 serait que le type de retour de fuite de transform peut être omis. Le erreur, cependant, reste le même, je suis heureux avec des solutions (seulement) travaillent en C++ 14 ainsi)

Répondre

2

dans votre exemple ici:..

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

Box est un modèle de classe , pas une classe. À l'intérieur de la classe, Box est le injecté-class-name, il se réfère toujours spécifiquement à Box<TContent>. Donc, si vous avez besoin de changer le tapez (comme transform peut très bien avoir besoin de faire), cela ne fonctionnera pas. Vous devez spécifier quiBox que vous voulez en fonction de ce Function est:

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

Le deuxième problème est quand vous l'appelez:

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

toTuple est un modèle fonction , pas une fonction . Vous ne pouvez pas le passer à un autre modèle de fonction car il n'a pas de type, il ne peut pas être déduit.Comme précédemment, vous devez spécifier quitoTuple vous voulez:

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

Ou envelopper le tout dans un lambda (ce qui est une mise en œuvre simplifiée qui ne se soucie pas des copies, des références ou SFINAE):

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

Merci. Pour une raison quelconque, C++ 11 et C++ 14 compilent joyeusement le decltype mal formé. Quoi qu'il en soit, le changer en 'Box ' le rendrait correct, non? – Qqwy

+0

Comme "toTuple" est un modèle de fonction, pas une fonction, vous ne pouvez pas le passer à un autre modèle de fonction car il n'a pas de type, il ne peut pas être déduit. " -> Y at-il des cas dans lesquels passer un modèle de fonction à un autre modèle de fonction où il peut être déduit? – Qqwy

+1

@Qqwy Oui, vous pourriez. Et non, il n'y en a pas. – Barry