Ich bin darüber gestolpert “Warum funktioniert der Template-Argumentabzug hier nicht?“ kürzlich und die Antworten lassen sich zu „Es ist ein nicht abgeleiteter Kontext“ zusammenfassen.
Insbesondere der erste sagt, dass es so ist, und leitet dann für “Details” zum Standard weiter, während der zweite den Standard zitiert, was gelinde gesagt kryptisch ist.
Kann jemand Normalsterblichen wie mir bitte erklären, was für eine nichtabgeleiteter Kontext ist, wann tritt es auf und warum tritt es auf?
Abzug bezieht sich auf den Prozess der Bestimmung des Typs eines Vorlagenparameters aus einem gegebenen Argument. Es gilt für Funktionsvorlagen, auto
, und einige andere Fälle (z. B. Teilspezialisierung). Betrachten Sie zum Beispiel:
template <typename T> void f(std::vector<T>);
Nun, wenn Sie sagen f(x)
wo Sie erklärt haben std::vector<int> x;
dann T
ist abgeleitet als int
und Sie erhalten die Spezialisierung f<int>
.
Damit die Ableitung funktioniert, muss der abzuleitende Vorlagenparametertyp in einem ableitenden Kontext erscheinen. In diesem Beispiel ist der Funktionsparameter von f
ist solch ein deduzierbarer Kontext. Das heißt, ein Argument im Funktionsaufrufausdruck ermöglicht es uns, den Vorlagenparameter zu bestimmen T
sollte sein, damit der Aufrufausdruck gültig ist.
Allerdings gibt es auch nicht-abgeleitete Kontexte, in denen keine Ableitung möglich ist. Das kanonische Beispiel ist „ein Vorlagenparameter, der links von a erscheint ::
:
template <typename> struct Foo;
template <typename T> void g(typename Foo<T>::type);
In dieser Funktionsvorlage wird die T
in der Funktionsparameterliste befindet sich in einem nicht abgeleiteten Kontext. So kann man das nicht sagen g(x)
und ableiten T
. Der Grund dafür ist, dass zwischen beliebigen Typen und keine “Rückwärtskorrespondenz” besteht Mitglieder Foo<T>::type
. Zum Beispiel könnten Sie Spezialisierungen haben:
template <> struct Foo<int> { using type = double; };
template <> struct Foo<char> { using type = double; };
template <> struct Foo<float> { using type = bool; };
template <> struct Foo<long> { int type = 10; };
template <> struct Foo<unsigned> { };
Wenn Sie anrufen g(double{})
es gibt zwei mögliche Antworten für T
und wenn Sie anrufen g(int{})
Es gibt keine Antwort. Im Allgemeinen gibt es keine Beziehung zwischen Klassenvorlagenparametern und Klassenmitgliedern, sodass Sie keine vernünftige Argumentableitung durchführen können.
Gelegentlich ist es sinnvoll, die Argumentableitung explizit zu unterbinden. Dies ist beispielsweise der Fall bei std::forward
. Ein weiteres Beispiel ist, wenn Sie Conversions von haben Foo<U>
zu Foo<T>
sagen wir, oder andere Konvertierungen (think std::string
und char const *
). Angenommen, Sie haben eine freie Funktion:
template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);
Wenn Sie anrufen binary_function(t, u)
, dann kann die Deduktion mehrdeutig sein und somit scheitern. Aber es ist vernünftig, nur ein Argument und abzuleiten nicht den anderen ableiten und so implizite Konvertierungen zulassen. Jetzt wird ein explizit nicht abgeleiteter Kontext benötigt, zum Beispiel so:
template <typename T>
struct type_identity {
using type = T;
};
template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
return binary_function(lhs, rhs);
}
(Möglicherweise haben Sie solche Abzugsprobleme mit etwas wie erlebt std::min(1U, 2L)
.)
Verwandte: C++, Template-Argument kann nicht abgeleitet werden
– Shafik Yaghmour
11. August 2014 um 14:22 Uhr