Je sais que le code ci-dessous est une spécialisation partielle d'une classe:
template <typename T1, typename T2>
class MyClass {
…
};
// partial specialization: both template parameters have same type
template <typename T>
class MyClass<T,T> {
…
};
Je sais également que C ++ n'autorise pas la spécialisation partielle du modèle de fonction (seule la totalité est autorisée). Mais mon code signifie-t-il que j'ai partiellement spécialisé mon modèle de fonction pour un / même type d'arguments? Parce que cela fonctionne pour Microsoft Visual Studio 2010 Express! Si non, pouvez-vous expliquer le concept de spécialisation partielle?
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template <typename T1, typename T2>
inline T1 max (T1 const& a, T2 const& b)
{
return a < b ? b : a;
}
template <typename T>
inline T const& max (T const& a, T const& b)
{
return 10;
}
int main ()
{
cout << max(4,4.2) << endl;
cout << max(5,5) << endl;
int z;
cin>>z;
}
max(5,5)
résoutmax(T const&, T const&) [with T=int]
et nonmax(T1 const&, T2 const&) [with T1=int and T2=int]
?Réponses:
La spécialisation partielle des fonctions n'est pas encore autorisée selon la norme. Dans l'exemple, vous surchargez et ne spécialisez pas la
max<T1,T2>
fonction.Sa syntaxe aurait dû ressembler un peu à celle ci-dessous, si elle avait été autorisée:
// Partial specialization is not allowed by the spec, though! template <typename T> inline T const& max<T,T> (T const& a, T const& b) { ^^^^^ <--- [supposed] specializing here return 10; }
Dans le cas d'un modèle de fonction, seule la spécialisation complète est autorisée par le standard C ++, - à l'exclusion des extensions du compilateur!
la source
(T, T)
sur(T1, T2)
pour(int, int)
, c'est parce que le premier garantit qu'il y a 2 paramètres et que les deux types sont identiques; ce dernier garantit seulement qu'il y a 2 paramètres. Le compilateur choisit toujours une description précise. Par exemple, si vous devez faire un choix entre 2 descriptions d'une «rivière», laquelle choisiriez-vous? "collecte d'eau" vs "collecte d'eau qui coule".Étant donné que la spécialisation partielle n'est pas autorisée - comme l'ont indiqué d'autres réponses -, vous pouvez la contourner en utilisant
std::is_same
etstd::enable_if
, comme ci-dessous:template <typename T, class F> inline typename std::enable_if<std::is_same<T, int>::value, void>::type typed_foo(const F& f) { std::cout << ">>> messing with ints! " << f << std::endl; } template <typename T, class F> inline typename std::enable_if<std::is_same<T, float>::value, void>::type typed_foo(const F& f) { std::cout << ">>> messing with floats! " << f << std::endl; } int main(int argc, char *argv[]) { typed_foo<int>("works"); typed_foo<float>(2); }
Production:
$ ./a.out >>> messing with ints! works >>> messing with floats! 2
Edit : Au cas où vous auriez besoin de pouvoir traiter tous les autres cas restants, vous pouvez ajouter une définition indiquant que les cas déjà traités ne devraient pas correspondre - sinon vous tomberiez dans des définitions ambiguës. La définition pourrait être:
template <typename T, class F> inline typename std::enable_if<(not std::is_same<T, int>::value) and (not std::is_same<T, float>::value), void>::type typed_foo(const F& f) { std::cout << ">>> messing with unknown stuff! " << f << std::endl; } int main(int argc, char *argv[]) { typed_foo<int>("works"); typed_foo<float>(2); typed_foo<std::string>("either"); }
Ce qui produit:
$ ./a.out >>> messing with ints! works >>> messing with floats! 2 >>> messing with unknown stuff! either
Bien que cette chose dans tous les cas semble un peu ennuyeuse, puisque vous devez dire au compilateur tout ce que vous avez déjà fait, il est tout à fait faisable de traiter jusqu'à 5 ou quelques spécialisations supplémentaires.
la source
Si vous voulez vraiment comprendre les modèles, vous devriez jeter un œil aux langages fonctionnels. Le monde des modèles en C ++ est un sous-langage purement fonctionnel en soi.
Dans les langages fonctionnels, les sélections sont effectuées à l'aide du Pattern Matching :
-- An instance of Maybe is either nothing (None) or something (Just a) -- where a is any type data Maybe a = None | Just a -- declare function isJust, which takes a Maybe -- and checks whether it's None or Just isJust :: Maybe a -> Bool -- definition: two cases (_ is a wildcard) isJust None = False isJust Just _ = True
Comme vous pouvez le voir, nous surchargons la définition de
isJust
.Eh bien, les modèles de classe C ++ fonctionnent exactement de la même manière. Vous fournissez une déclaration principale , qui indique le nombre et la nature des paramètres. Cela peut être juste une déclaration, ou aussi agir comme une définition (votre choix), puis vous pouvez (si vous le souhaitez) fournir des spécialisations du modèle et leur associer une version différente (sinon ce serait idiot) de la classe .
Pour les fonctions de modèle, la spécialisation est un peu plus délicate: elle entre quelque peu en conflit avec la résolution de surcharge. En tant que tel, il a été décidé qu'une spécialisation se rapporterait à une version non spécialisée, et les spécialisations ne seraient pas prises en compte lors de la résolution de surcharge. Par conséquent, l'algorithme de sélection de la bonne fonction devient:
(pour un traitement en profondeur, voir GotW # 49 )
En tant que telle, la spécialisation des fonctions par modèle est un citoyen de seconde zone (littéralement). En ce qui me concerne, nous serions mieux sans eux: je n'ai pas encore rencontré de cas où l'utilisation d'une spécialisation de modèle ne pourrait pas être résolue avec une surcharge.
Non, c'est simplement une surcharge, et c'est très bien. En fait, les surcharges fonctionnent généralement comme nous l'attendons, tandis que les spécialisations peuvent être surprenantes (rappelez-vous l'article de GotW que j'ai lié).
la source
"As such, template specialization of functions is a second-zone citizen (literally). As far as I am concerned, we would be better off without them: I have yet to encounter a case where a template specialization use could not be solved with overloading instead."
Que diriez-vous des paramètres de modèle non type?boost::mpl::integral_c<unsigned, 3u>
. Une autre solution pourrait également être d'utiliserenable_if
/disable_if
, bien que ce soit une autre histoire.La spécialisation partielle non-classe et non variable n'est pas autorisée, mais comme indiqué:
L'ajout d'une classe pour transférer l'appel de fonction peut résoudre ce problème, voici un exemple:
template <class Tag, class R, class... Ts> struct enable_fun_partial_spec; struct fun_tag {}; template <class R, class... Ts> constexpr R fun(Ts&&... ts) { return enable_fun_partial_spec<fun_tag, R, Ts...>::call( std::forward<Ts>(ts)...); } template <class R, class... Ts> struct enable_fun_partial_spec<fun_tag, R, Ts...> { constexpr static R call(Ts&&... ts) { return {0}; } }; template <class R, class T> struct enable_fun_partial_spec<fun_tag, R, T, T> { constexpr static R call(T, T) { return {1}; } }; template <class R> struct enable_fun_partial_spec<fun_tag, R, int, int> { constexpr static R call(int, int) { return {2}; } }; template <class R> struct enable_fun_partial_spec<fun_tag, R, int, char> { constexpr static R call(int, char) { return {3}; } }; template <class R, class T2> struct enable_fun_partial_spec<fun_tag, R, char, T2> { constexpr static R call(char, T2) { return {4}; } }; static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, ""); static_assert(fun<int>(1, 1) == 2, ""); static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, ""); static_assert(fun<char>(1, 1) == 2, ""); static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, ""); static_assert(fun<long>(1L, 1L) == 1, ""); static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, ""); static_assert(fun<double>(1L, 1L) == 1, ""); static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, ""); static_assert(fun<int>(1u, 1) == 0, ""); static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, ""); static_assert(fun<char>(1, 'c') == 3, ""); static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, ""); static_assert(fun<unsigned>('c', 1) == 4, ""); static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, ""); static_assert(fun<unsigned>(10.0, 1) == 0, ""); static_assert( std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, ""); static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, ""); static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, ""); static_assert(fun<unsigned>() == 0, "");
la source
Non. Par exemple, vous pouvez vous spécialiser légalement
std::swap
, mais vous ne pouvez pas légalement définir votre propre surcharge. Cela signifie que vous ne pouvez pas fairestd::swap
fonctionner votre propre modèle de classe personnalisé.La surcharge et la spécialisation partielle peuvent avoir le même effet dans certains cas, mais loin de tout.
la source
swap
surcharge dans votre espace de noms.Réponse tardive, mais certains lecteurs tardifs pourraient la trouver utile: parfois, une fonction d'assistance - conçue de manière à pouvoir être spécialisée - peut également résoudre le problème.
Alors imaginons, c'est ce que nous avons essayé de résoudre:
template <typename R, typename X, typename Y> void function(X x, Y y) { R* r = new R(x); f(r, y); // another template function? } // for some reason, we NEED the specialization: template <typename R, typename Y> void function<R, int, Y>(int x, Y y) { // unfortunately, Wrapper has no constructor accepting int: Wrapper* w = new Wrapper(); w->setValue(x); f(w, y); }
OK, spécialisation partielle de la fonction de modèle, on ne peut pas faire ça ... Alors "exportons" la partie nécessaire à la spécialisation dans une fonction d'aide, spécialisons-la et utilisons-la:
template <typename R, typename T> R* create(T t) { return new R(t); } template <> Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal... { Wrapper* w = new Wrapper(); w->setValue(n); return w; } template <typename R, typename X, typename Y> void function(X x, Y y) { R* r = create<R>(x); f(r, y); // another template function? }
Cela peut être intéressant surtout si les alternatives (surcharges normales au lieu de spécialisations, la solution de contournement proposée par Rubens, ... - non pas que celles-ci soient mauvaises ou que la mienne soit meilleure, juste une autre) partageraient beaucoup de code commun.
la source