Paragraph 14.8.2/8 des C++11-Standards legt die Bedingungen fest, unter denen ein Substitutionsfehler zu einem “harten” Kompilierungsfehler (wodurch die Kompilierung fehlschlägt) oder zu einem “weichen” Fehler führen soll oder nicht Veranlassen Sie den Compiler, eine Vorlage aus einer Reihe von Kandidaten für die Auflösung von Überladungen zu verwerfen (ohne dass die Kompilierung fehlschlägt und das bekannte SFINAE-Idiom aktiviert wird):
Wenn eine Ersetzung zu einem ungültigen Typ oder Ausdruck führt, schlägt die Typableitung fehl. Ein ungültiger Typ oder Ausdruck wäre falsch formatiert, wenn er mit den ersetzten Argumenten geschrieben würde. [ Note: Access checking is done as
part of the substitution process. —end note ] Nur ungültige Typen und Ausdrücke im unmittelbaren Kontext des Funktionstyps und seiner Vorlagenparametertypen können zu einem Abzugsfehler führen. […]
Die Wörter “unmittelbaren Kontext” kommen im gesamten C++11-Standard nur 8 Mal vor, und jedes Mal folgt (oder tritt als Teil davon) eine Instanz des folgenden (nicht normativen) Textes auf:
[Note: The evaluation
of the substituted types and expressions can result in side effects such as the instantiation of class template
specializations and/or function template specializations, the generation of implicitly-defined functions, etc.
Such side effects are not in the “immediate context” and can result in the program being ill-formed.—end
note ]
Der Hinweis gibt einen (nicht sehr großzügigen) Hinweis darauf, was gemeint ist unmittelbaren Kontextaber zumindest für mich reicht das oft nicht aus, um zu entscheiden, ob eine Substitution einen “harten” Kompilierungsfehler verursachen soll oder nicht.
FRAGE:
Können Sie eine Erklärung, ein Entscheidungsverfahren und/oder einige konkrete Beispiele geben, um herauszufinden, in welchen Fällen ein Substitutionsfehler in der “unmittelbaren Kontext” des Funktionstyps und seiner Vorlagenparametertypen?
Wenn Sie alle Vorlagen und implizit definierten Funktionen berücksichtigen, die zum Bestimmen des Ergebnisses der Vorlagenargumentsubstitution erforderlich sind, und sich vorstellen, dass sie zuerst generiert werden, bevor die Ersetzung beginnt, dann stehen alle Fehler, die in diesem ersten Schritt auftreten, nicht im unmittelbaren Kontext. und zu harten Fehlern führen.
Wenn all diese Instanziierungen und impliziten Definitionen (die das Definieren von Funktionen als gelöscht enthalten können) fehlerfrei durchgeführt werden können, dann werden alle weiteren “Fehler”, die während der Substitution auftreten (dh beim Verweisen auf die instanziierten Vorlagen und implizit definierten Funktionen in den Funktionsvorlagen Unterschrift) sind keine Fehler, sondern führen zu Abzugsfehlern.
Also gegeben eine Funktionsvorlage wie diese:
template<typename T>
void
func(typename T::type* arg);
und ein “Fallback”, das verwendet wird, wenn der Abzug für die andere Funktion fehlschlägt:
template<typename>
void
func(...);
und eine Klassenvorlage wie diese:
template<typename T>
struct A
{
typedef T* type;
};
Ein Anruf bei func<A<int&>>(nullptr)
wird ersetzen A<int&>
zum T
und um zu prüfen, ob T::type
existiert, muss es instanziiert werden A<int&>
. Stellen wir uns vor, eine explizite Instanziierung vor den Aufruf von zu setzen func<A<int&>(nullptr)
:
template class A<int&>;
dann würde das fehlschlagen, weil es versucht, den Typ zu erstellen int&*
und Verweise auf Referenzen sind nicht erlaubt. Wir kommen nicht zu dem Punkt, an dem wir prüfen, ob die Substitution erfolgreich ist, weil es einen schwerwiegenden Fehler bei der Instanziierung gibt A<int&>
.
Nehmen wir nun an, es gibt eine explizite Spezialisierung von A
:
template<>
struct A<char>
{
};
Ein Anruf bei func<A<char>>(nullptr)
erfordert die Instanziierung von A<char>
stellen Sie sich also eine explizite Instanziierung irgendwo im Programm vor dem Aufruf vor:
template class A<char>;
Diese Instanziierung ist in Ordnung, es gibt keinen Fehler, also fahren wir mit der Argumentsubstitution fort. Die Instantiierung von A<char>
hat funktioniert, aber A<char>::type
existiert nicht, aber das ist in Ordnung, weil es nur in der Deklaration von referenziert wird func
führt also nur dazu, dass der Argumentabzug fehlschlägt, und der Fallback ...
Funktion wird stattdessen aufgerufen.
In anderen Situationen kann die Substitution dazu führen, dass spezielle Elementfunktionen implizit definiert werden, möglicherweise als gelöscht, was andere Instanziierungen oder implizite Definitionen auslösen kann. Wenn Fehler während dieser Phase “Generieren von Instanziierungen und impliziten Definitionen” auftreten, dann sind sie Fehler, aber wenn dies gelingt, aber während der Ersetzung erweist sich ein Ausdruck in der Signatur der Funktionsvorlage als ungültig, z. B. weil sie ein nicht vorhandenes Element verwendet oder Etwas, das implizit als gelöscht definiert wurde, ist kein Fehler, sondern nur ein Deduktionsfehler.
Das mentale Modell, das ich verwende, ist also, dass die Substitution zuerst einen “Vorbereitungsschritt” durchführen muss, um Typen und Member zu generieren, was schwerwiegende Fehler verursachen kann, aber sobald wir alle erforderlichen Generierungen durchgeführt haben, sind alle weiteren ungültigen Verwendungen keine Fehler. All dies bewirkt natürlich nur, dass das Problem von „was tut unmittelbaren Kontext meinst?” bis “Welche Typen und Member müssen generiert werden, bevor diese Ersetzung überprüft werden kann?”, also kann es Ihnen helfen oder auch nicht!
Die unmittelbaren Kontext ist im Grunde das, was Sie in der Vorlagendeklaration selbst sehen. Alles darüber hinaus ist ein harter Fehler. Beispiele für schwere Fehler:
#include <type_traits>
template<class T>
struct trait{ using type = typename T::type; };
template<class T, class U = typename trait<T>::type>
void f(int);
void f(...);
template<class T, class U = typename T::type>
void g(int);
void g(...);
template<class>
struct dependent_false : std::false_type{};
template<class T>
struct X{
static_assert(dependent_false<T>(), "...");
using type = void;
};
int main(){
f<int>(0);
g<X<int>>(0);
}
Live-Version.
Siehe auch Lösung des SFINAE-Problems für Ausdrücke.
– Jess Gut
7. März 2013 um 0:26 Uhr
@JesseGood: Ja, das habe ich gelesen, aber es hilft nur teilweise und die Aufzählungsliste ist nicht sehr klar
– Andy Prowl
7. März 2013 um 0:30 Uhr
Zugehörige DR: `Definieren von „unmittelbarem Kontext“
– Nawaz
2. Mai 2018 um 14:02 Uhr