Ich weiß, dass der folgende Code eine teilweise Spezialisierung einer Klasse ist:
template <typename T1, typename T2>
class MyClass {
…
};
// partial specialization: both template parameters have same type
template <typename T>
class MyClass<T,T> {
…
};
Ich weiß auch, dass C ++ keine teilweise Spezialisierung von Funktionsvorlagen zulässt (nur vollständige ist zulässig). Aber bedeutet mein Code, dass ich meine Funktionsvorlage teilweise auf ein / denselben Typargumente spezialisiert habe? Weil es für Microsoft Visual Studio 2010 Express funktioniert! Wenn nein, können Sie bitte das Konzept der Teilspezialisierung erläutern?
#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)
aufmax(T const&, T const&) [with T=int]
und nichtmax(T1 const&, T2 const&) [with T1=int and T2=int]
?Antworten:
Eine teilweise Teilspezialisierung ist nach Standard noch nicht zulässig. In diesem Beispiel überladen Sie die
max<T1,T2>
Funktion tatsächlich und spezialisieren sie nicht .Die Syntax hätte ungefähr so aussehen sollen, wenn es erlaubt gewesen wäre:
// 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; }
Bei Funktionsvorlagen ist nach C ++ - Standard nur eine vollständige Spezialisierung zulässig - mit Ausnahme der Compiler-Erweiterungen!
quelle
(T, T)
über(T1, T2)
für(int, int)
, ist , weil die früheren Garantien , dass es 2 Parameter und beiden Typen sind gleich; Letzteres garantiert nur, dass es 2 Parameter gibt. Der Compiler wählt immer eine genaue Beschreibung. zB Wenn Sie zwischen 2 Beschreibungen eines "Flusses" wählen müssen, welche würden Sie wählen? "Sammlung von Wasser" vs "Sammlung von fließendem Wasser".Da eine teilweise Spezialisierung nicht zulässig ist - wie andere Antworten zeigten -, können Sie sie mit
std::is_same
undstd::enable_if
wie folgt umgehen: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); }
Ausgabe:
$ ./a.out >>> messing with ints! works >>> messing with floats! 2
Bearbeiten : Falls Sie in der Lage sein müssen, alle anderen verbleibenden Fälle zu behandeln, können Sie eine Definition hinzufügen, die besagt, dass bereits behandelte Fälle nicht übereinstimmen sollten - andernfalls würden Sie in mehrdeutige Definitionen geraten. Die Definition könnte sein:
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"); }
Welches produziert:
$ ./a.out >>> messing with ints! works >>> messing with floats! 2 >>> messing with unknown stuff! either
Obwohl diese All-Case- Sache etwas langweilig aussieht, ist es durchaus machbar, bis zu 5 oder ein paar weitere Spezialisierungen zu behandeln, da Sie dem Compiler alles mitteilen müssen, was Sie bereits getan haben.
quelle
Wenn Sie Vorlagen wirklich verstehen möchten, sollten Sie sich funktionale Sprachen ansehen. Die Welt der Vorlagen in C ++ ist eine rein funktionale Subsprache für sich.
In funktionalen Sprachen erfolgt die Auswahl mithilfe von 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
Wie Sie sehen können, überladen wir die Definition von
isJust
.Nun, C ++ - Klassenvorlagen funktionieren genauso. Sie stellen eine Haupterklärung, dass die Anzahl und die Art des Parameters erklärt. Es kann nur eine Deklaration sein oder auch als Definition dienen (Ihre Wahl), und dann können Sie (wenn Sie dies wünschen) Spezialisierungen des Musters bereitstellen und ihnen eine andere (sonst alberne) Version der Klasse zuordnen .
Bei Vorlagenfunktionen ist die Spezialisierung etwas umständlicher: Sie steht in Konflikt mit der Überlastungsauflösung. Daher wurde entschieden, dass sich eine Spezialisierung auf eine nicht spezialisierte Version bezieht und Spezialisierungen bei der Überlastungsauflösung nicht berücksichtigt werden. Daher lautet der Algorithmus zur Auswahl der richtigen Funktion:
(Für eine eingehende Behandlung siehe GotW # 49 )
Daher ist die Vorlagenspezialisierung von Funktionen (im wahrsten Sinne des Wortes) ein Bürger der zweiten Zone. Meiner Meinung nach wären wir ohne sie besser dran: Ich habe noch keinen Fall gefunden, in dem eine Verwendung der Vorlagenspezialisierung nicht durch Überladung gelöst werden konnte.
Nein, es ist einfach eine Überlastung, und das ist in Ordnung. Tatsächlich funktionieren Überladungen normalerweise so, wie wir es erwarten, während Spezialisierungen überraschend sein können (denken Sie an den GotW-Artikel, den ich verlinkt habe).
quelle
"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."
Wie wäre es mit nicht typisierten Vorlagenparametern?boost::mpl::integral_c<unsigned, 3u>
. Eine andere Lösung könnte auch sein,enable_if
/ zu verwendendisable_if
, obwohl es eine andere Geschichte ist.Eine nicht klassifizierte, nicht variable Teilspezialisierung ist nicht zulässig, aber wie gesagt:
Das Hinzufügen einer Klasse zum Weiterleiten des Funktionsaufrufs kann dieses Problem lösen. Hier ein Beispiel:
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, "");
quelle
Nein. Sie können sich beispielsweise legal spezialisieren
std::swap
, aber Ihre eigene Überlastung nicht legal definieren. Das bedeutet, dass Sie nichtstd::swap
für Ihre eigene benutzerdefinierte Klassenvorlage arbeiten können.Überladung und teilweise Spezialisierung können in einigen Fällen den gleichen Effekt haben, sind aber bei weitem nicht alles.
quelle
swap
Überladung in Ihren Namespace.Späte Antwort, aber einige späte Leser finden es möglicherweise nützlich: Manchmal kann auch eine Hilfsfunktion, die so konzipiert ist, dass sie spezialisiert werden kann, das Problem lösen.
Stellen wir uns also vor, wir haben versucht , Folgendes zu lösen:
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, teilweise Spezialisierung auf Vorlagenfunktionen, das können wir nicht ... Also "exportieren" wir den für die Spezialisierung benötigten Teil in eine Hilfsfunktion, spezialisieren diesen und verwenden ihn:
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? }
Dies kann interessant sein , vor allem , wenn die Alternativen (normal Überlastungen statt Spezialisierungen, die Abhilfe durch Rubens vorgeschlagen, ... - nicht , dass diese schlecht sind oder meine ist besser, nur ein anderer ein) eine ganze Reihe von gemeinsamen Code teilen würde.
quelle