/Zc:twoPhase- (Zwei-Phasen-Namenssuche deaktivieren)

Die /Zc:twoPhase- Option weist den Compiler an /permissive-, das ursprüngliche, nicht konforme Microsoft C++-Compilerverhalten zum Analysieren und Instanziieren von Klassenvorlagen und Funktionsvorlagen zu verwenden.

Syntax

/Zc:twoPhase-

Hinweise

Visual Studio 2017, Version 15.3 und höher: Unter /permissive-, verwendet der Compiler die Zwei-Phasen-Namenssuche für die Auflösung von Vorlagennamen. Wenn Sie auch angeben /Zc:twoPhase-, wird der Compiler auf die vorherige nicht konforme Klassenvorlage und das Auflösungs- und Ersetzungsverhalten der Funktionsvorlagen zurückgesetzt. Wenn /permissive- nicht angegeben ist, ist das nicht konforme Verhalten der Standard.

Die Windows SDK-Headerdateien in Version 10.0.15063.0 (Creators Update oder RS2) und frühere Versionen funktionieren nicht im Konformitätsmodus. /Zc:twoPhase- ist erforderlich, um Code für diese SDK-Versionen zu kompilieren, wenn Sie es verwenden /permissive-. Versionen des Windows SDK ab Version 10.0.15254.0 (Fall Creators Update oder RS3) funktionieren ordnungsgemäß im Konformitätsmodus. Sie benötigen /Zc:twoPhase- die Option nicht.

Verwenden Sie diese Option /Zc:twoPhase- , wenn ihr Code das alte Verhalten benötigt, um ordnungsgemäß zu kompilieren. Erwägen Sie dringend, Ihren Code so zu aktualisieren, dass er dem Standard entspricht.

Compilerverhalten unter /Zc:twoPhase-

Standardmäßig oder in Visual Studio 2017, Version 15.3 und höher, wenn Sie beide /permissive- angeben und /Zc:twoPhase-, verwendet der Compiler dieses Verhalten:

  • Es analysiert nur die Vorlagendeklaration, den Klassenkopf und die Basisklassenliste. Der Vorlagentext wird als Tokenstream erfasst. Es werden keine Funktionstexte, Initialisierer, Standardargumente oder noexcept-Argumente analysiert. Die Klassenvorlage wird für einen vorläufigen Typ pseudo instanziiert, um zu überprüfen, ob die Deklarationen in der Klassenvorlage korrekt sind. Betrachten Sie diese Klassenvorlage:

    template <typename T> class Derived : public Base<T> { ... }
    

    Die Vorlagendeklaration, template <typename T>der Klassenkopf class Derivedund die Basisklassenliste public Base<T> werden analysiert, der Vorlagentext wird jedoch als Tokenstream erfasst.

  • Wenn sie eine Funktionsvorlage analysiert, analysiert der Compiler nur die Funktionssignatur. Der Funktionstext wird nie analysiert. Stattdessen wird sie als Tokenstream erfasst.

Wenn der Textkörper der Vorlage Syntaxfehler aufweist, die Vorlage jedoch nie instanziiert wird, wird der Compiler die Fehler nicht diagnostizieren.

Ein weiterer Effekt dieses Verhaltens ist die Überlastungsauflösung. Nicht standardmäßiges Verhalten tritt auf, da der Tokendatenstrom an der Instanziierungswebsite erweitert wird. Symbole, die an der Vorlagendeklaration nicht sichtbar waren, können an der Instanziierungsstelle sichtbar sein. Das bedeutet, dass sie an der Überladungsauflösung teilnehmen können. Möglicherweise finden Sie vorlagenänderungsverhalten basierend auf Code, der bei der Vorlagendefinition nicht sichtbar war, im Gegensatz zum Standard.

Betrachten Sie z. B. diesen Code:

// zctwophase.cpp
// To test options, compile by using
// cl /EHsc /nologo /W4 zctwophase.cpp
// cl /EHsc /nologo /W4 /permissive- zctwophase.cpp
// cl /EHsc /nologo /W4 /permissive- /Zc:twoPhase- zctwophase.cpp

#include <cstdio>

void func(long) { std::puts("Standard two-phase") ;}

template<typename T> void g(T x)
{
    func(0);
}

void func(int) { std::puts("Microsoft one-phase"); }

int main()
{
    g(6174);
}

Hier sehen Sie die Ausgabe, wenn Sie den Standardmodus, den Konformitätsmodus und den Konformitätsmodus mit /Zc:twoPhase- Compileroptionen verwenden:

C:\Temp>cl /EHsc /nologo /W4 zctwophase.cpp && zctwophase
zctwophase.cpp
Microsoft one-phase

C:\Temp>cl /EHsc /nologo /W4 /permissive- zctwophase.cpp && zctwophase
zctwophase.cpp
Standard two-phase

C:\Temp>cl /EHsc /nologo /W4 /permissive- /Zc:twoPhase- zctwophase.cpp && zctwophase
zctwophase.cpp
Microsoft one-phase

Bei der Kompilierung im Konformitätsmodus unter /permissive-druckt dieses Programm "Standard two-phase", da die zweite Überladung func nicht sichtbar ist, wenn der Compiler die Vorlage erreicht. Wenn Sie es hinzufügen /Zc:twoPhase-, druckt das Programm "Microsoft one-phase". Die Ausgabe entspricht dem, wenn Sie nicht angeben /permissive-.

Abhängige Namen sind Namen , die von einem Vorlagenparameter abhängen. Diese Namen weisen das Nachschlageverhalten auf, das sich ebenfalls unterscheidet /Zc:twoPhase-. Im Konformitätsmodus sind abhängige Namen nicht an den Punkt der Definition der Vorlage gebunden. Stattdessen sucht der Compiler sie nach, wenn sie die Vorlage instanziiert. Bei Funktionsaufrufen mit einem abhängigen Funktionsnamen wird der Name an Funktionen gebunden, die in der Vorlagendefinition auf der Aufrufwebsite sichtbar sind. Weitere Überladungen aus argumentabhängigen Nachschlagevorgängen werden sowohl an der Stelle der Vorlagendefinition als auch am Punkt der Vorlageninstanziierung hinzugefügt.

Die zweistufige Suche besteht aus zwei Teilen: Der Nachschlagevorgang für nicht abhängige Namen während der Vorlagendefinition und die Suche nach abhängigen Namen während der Vorlageninstanziierung. Unter /Zc:twoPhase-dem Compiler wird die argumentabhängige Suche nicht separat von nicht qualifizierten Nachschlagevorgängen ausgeführt. Das heißt, es wird keine Zwei-Phasen-Suche ausgeführt, sodass die Ergebnisse der Überladungsauflösung unterschiedlich sein können.

Hier ist ein weiteres Beispiel:

// zctwophase1.cpp
// To test options, compile by using
// cl /EHsc /W4 zctwophase1.cpp
// cl /EHsc /W4 /permissive- zctwophase1.cpp
// cl /EHsc /W4 /permissive- /Zc:twoPhase- zctwophase1.cpp

#include <cstdio>

void func(long) { std::puts("func(long)"); }

template <typename T> void tfunc(T t) {
    func(t);
}

void func(int) { std::puts("func(int)"); }

namespace NS {
    struct S {};
    void func(S) { std::puts("NS::func(NS::S)"); }
}

int main() {
    tfunc(1729);
    NS::S s;
    tfunc(s);
}

Wenn er ohne /permissive-kompiliert wird, wird dieser Code gedruckt:

func(int)
NS::func(NS::S)

Bei der Kompilierung mit /permissive-, aber ohne /Zc:twoPhase-, wird dieser Code gedruckt:

func(long)
NS::func(NS::S)

Beim Kompilieren mit beidem /permissive- und /Zc:twoPhase-diesem Code wird Folgendes gedruckt:

func(int)
NS::func(NS::S)

Im Konformitätsmodus unter /permissive-, wird der Aufruf tfunc(1729) in die void func(long) Überladung aufgelöst. Es wird nicht wie void func(int) unter der /Zc:twoPhase-Überladung aufgelöst. Der Grund ist, dass die nicht qualifizierten Nachschlagevorgänge func(int) nach der Definition der Vorlage deklariert werden und nicht durch argumentabhängige Suche gefunden werden. Nimmt jedoch void func(S) an argumentabhängigen Nachschlagevorgängen teil, sodass er dem Überladungssatz für den Aufruf tfunc(s)hinzugefügt wird, obwohl er nach der Funktionsvorlage deklariert wird.

Aktualisieren des Codes für die Zweiphasenkonformität

Für ältere Versionen des Compilers sind die Schlüsselwörter template nicht erforderlich, und typename überall, wo der C++-Standard sie benötigt. Diese Schlüsselwörter werden in einigen Positionen benötigt, um zu unterscheiden, wie Compiler während der ersten Nachschlagephase einen abhängigen Namen analysieren sollten. Zum Beispiel:

T::Foo<a || b>(c);

Ein konformer Compiler analysiert Foo als Variable im Bereich von T, d. h. dieser Code ist ein logischer Oder Ausdruck, der T::foo < a als linker Operand und b > (c) als rechte Operand verwendet wird. Wenn Sie als Funktionsvorlage verwenden Foo möchten, müssen Sie angeben, dass es sich um eine Vorlage handelt, indem Sie das template Schlüsselwort hinzufügen:

T::template Foo<a || b>(c);

In Versionen von Visual Studio 2017, Version 15.3 und höher, wenn /permissive- und /Zc:twoPhase- werden angegeben, lässt der Compiler diesen Code ohne das template Schlüsselwort zu. Er interpretiert den Code als Aufruf einer Funktionsvorlage mit einem Argument von a || b, da er nur Vorlagen auf begrenzte Weise analysiert. Der obige Code wird in der ersten Phase nicht analysiert. Während der zweiten Phase gibt es genügend Kontext, um zu erkennen, dass T::Foo es sich um eine Vorlage und nicht um eine Variable handelt, sodass der Compiler die Verwendung des Schlüsselworts nicht erzwingt.

Dieses Verhalten kann auch angezeigt werden, indem das Schlüsselwort typename vor Namen in Funktionsvorlagentexten, Initialisierern, Standardargumenten und noexcept-Argumenten entfernt wird. Zum Beispiel:

template<typename T>
typename T::TYPE func(typename T::TYPE*)
{
    /* typename */ T::TYPE i;
}

Wenn Sie das Schlüsselwort typename nicht im Funktionstext verwenden, wird dieser Code unter /permissive- /Zc:twoPhase-, aber nicht /permissive- allein kompiliert. Das typename Schlüsselwort ist erforderlich, um anzugeben, dass dies TYPE abhängig ist. Da der Textkörper nicht analysiert /Zc:twoPhase-wird, benötigt der Compiler das Schlüsselwort nicht. Im /permissive- Konformitätsmodus generiert Code ohne das typename Schlüsselwort Fehler. Um Ihren Code zur Konformität in Visual Studio 2017, Version 15.3 und darüber hinaus, zu migrieren, fügen Sie das typename Schlüsselwort ein, in dem es fehlt.

Beachten Sie in ähnlicher Weise dieses Codebeispiel:

template<typename T>
typename T::template X<T>::TYPE func(typename T::TYPE)
{
    typename T::/* template */ X<T>::TYPE i;
}

Unter /permissive- /Zc:twoPhase- und in älteren Compilern erfordert der Compiler nur das template Schlüsselwort in Zeile 2. Im Konformitätsmodus erfordert der Compiler jetzt auch das template Schlüsselwort in Zeile 4, um anzugeben, dass T::X<T> es sich um eine Vorlage handelt. Suchen Sie nach Code, der dieses Schlüsselwort fehlt, und geben Sie ihn an, damit Ihr Code dem Standard entspricht.

Weitere Informationen zu Konformitätsproblemen finden Sie unter C++-Konformitätsverbesserungen in Visual Studio und Nichtstandardverhalten.

So legen Sie diese Compileroption in der Visual Studio-Entwicklungsumgebung fest

  1. Öffnen Sie das Dialogfeld Eigenschaftenseiten des Projekts. Weitere Informationen erhalten Sie unter Set C++ compiler and build properties in Visual Studio (Festlegen der Compiler- und Buildeigenschaften (C++) in Visual Studio).

  2. Klicken Sie auf der Eigenschaftenseite auf Konfigurationseigenschaften>C/C++>Befehlszeile.

  3. Ändern Sie die Eigenschaft "Zusätzliche Optionen", um sie einzuschließen/Zc:twoPhase-, und wählen Sie dann "OK" aus.

Siehe auch

/Zc (Konformität)