Compartir a través de


/Zc:twoPhase- (deshabilite la búsqueda de nombres en dos fases)

La /Zc:twoPhase- opción, en /permissive-, indica al compilador que use el comportamiento original y no conforme del compilador de Microsoft C++ para analizar y crear instancias de plantillas de clase y plantillas de función.

Sintaxis

/Zc:twoPhase-

Comentarios

Visual Studio 2017, versión 15.3 y posteriores: en /permissive-, el compilador usa la búsqueda de nombres en dos fases para la resolución de nombres de plantilla. Si también especifica /Zc:twoPhase-, el compilador revierte a su plantilla de clase anterior no conforme y el comportamiento de sustitución y resolución de nombres de plantilla de función. Cuando /permissive- no se especifica, el comportamiento no conforme es el valor predeterminado.

Los archivos de encabezado de Windows SDK de la versión 10.0.15063.0 (Creators Update o RS2) y versiones anteriores no funcionan en modo de conformidad. /Zc:twoPhase- es necesario compilar código para esas versiones del SDK cuando se usa /permissive-. Las versiones de Windows SDK a partir de la 10.0.15254.0 (Fall Creators Update o RS3) funcionan correctamente en modo de conformidad. No requieren la /Zc:twoPhase- opción .

Use /Zc:twoPhase- si el código requiere que el comportamiento anterior se compile correctamente. Considere seriamente la posibilidad de actualizar el código para que se ajuste al estándar.

Comportamiento del compilador en /Zc:twoPhase-

De forma predeterminada, o en Visual Studio 2017, versión 15.3 y posteriores, al especificar y /permissive- /Zc:twoPhase-, el compilador usa este comportamiento:

  • Analiza solo la declaración de plantilla, el encabezado de clase y la lista de clases base. El cuerpo de la plantilla se captura como secuencia de tokens. No se analizan los cuerpos de función, los inicializadores, los argumentos predeterminados ni los argumentos noexcept. Se crea una pseudo-instancia de la plantilla de clase en un tipo provisional para validar que las declaraciones de la plantilla de clase sean correctas. Considere esta plantilla de clase:

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

    Se analizan la declaración de plantilla, template <typename T>, el encabezado de clase class Derived y la lista de clases base public Base<T>, pero el cuerpo de la plantilla se captura como secuencia de tokens.

  • Cuando analiza una plantilla de función, el compilador solo analiza la firma de la función. El cuerpo de la función no se analiza nunca. En su lugar, se captura como secuencia de tokens.

Como resultado, si el cuerpo de la plantilla tiene errores de sintaxis, pero nunca se crean instancias de la plantilla, el compilador no diagnostica los errores.

Otro efecto de este comportamiento se observa en la resolución de sobrecargas. Se produce un comportamiento no estándar debido a la forma en que la secuencia de tokens se expande en el sitio de creación de instancias. Puede que algunos símbolos que no estaban visibles en la declaración de plantilla sean visibles en el punto de creación de instancias. Esto significa que pueden participar en la resolución de sobrecargas. Es posible que las plantillas cambien de comportamiento en función del código que no estaba visible en la definición de plantilla, contrariamente al estándar.

Por ejemplo, considere este código:

// 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);
}

Esta es la salida cuando se usa el modo predeterminado, el modo de conformidad y el modo de conformidad con /Zc:twoPhase- las opciones del compilador:

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

Cuando se compila en modo de conformidad en /permissive-, este programa imprime "Standard two-phase", porque la segunda sobrecarga de func no es visible cuando el compilador alcanza la plantilla. Si agrega /Zc:twoPhase-, el programa imprime "Microsoft one-phase". La salida es la misma que cuando no se especifica /permissive-.

Los nombres dependientes son aquellos que dependen de un parámetro de plantilla. Estos nombres tienen un comportamiento de búsqueda que también es diferente en /Zc:twoPhase-. En el modo de conformidad, los nombres dependientes no se enlazan en el punto de definición de la plantilla. En su lugar, el compilador los busca cuando crea una instancia de la plantilla. Para las llamadas a función con un nombre de función dependiente, el nombre se enlaza a las funciones visibles en el sitio de llamada de la definición de plantilla. Se agregan otras sobrecargas de la búsqueda dependiente del argumento, tanto en el punto de la definición de plantilla como en el punto de creación de instancias de plantilla.

La búsqueda en dos fases consta de dos partes: la búsqueda de nombres no dependientes durante la definición de plantilla y la búsqueda de nombres dependientes durante la creación de instancias de la plantilla. En /Zc:twoPhase-, el compilador no realiza la búsqueda dependiente del argumento por separado de la búsqueda no calificada. Es decir, no realiza una búsqueda en dos fases, por lo que los resultados de la resolución de sobrecargas pueden ser distintos.

A continuación se muestra otro ejemplo:

// 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);
}

Cuando se compila sin /permissive-, este código imprime:

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

Cuando se compila con /permissive-, pero sin /Zc:twoPhase-, este código imprime:

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

Cuando se compila con y /permissive- /Zc:twoPhase-, este código imprime:

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

En el modo de conformidad en /permissive-, la llamada tfunc(1729) se resuelve en la void func(long) sobrecarga. No se resuelve en la void func(int) sobrecarga, como en /Zc:twoPhase-. El motivo es que el valor no completo func(int) se declara después de la definición de la plantilla y no se encuentra a través de la búsqueda dependiente del argumento. Pero void func(S) participa en la búsqueda dependiente del argumento, por lo que se agrega al conjunto de sobrecargas para la llamada tfunc(s), aunque se declare después de la plantilla de función.

Actualización del código para la conformidad con las dos fases

Las versiones anteriores del compilador no requieren las palabras clave template y typename en todas partes donde las requiere el estándar de C++. Estas palabras clave son necesarias en algunas posiciones para eliminar la ambigüedad respecto a la forma en que los compiladores deben analizar un nombre dependiente durante la primera fase de la búsqueda. Por ejemplo:

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

Un compilador conforme analiza Foo como variable en el ámbito de T, lo que significa que este código es una expresión de OR lógico con T::foo < a como operando izquierdo y b > (c) como operando derecho. Si quiere usar Foo como plantilla de función, agregue la palabra clave template para indicar que es una plantilla:

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

En las versiones de Visual Studio 2017, versión 15.3 y posteriores, cuando /permissive- se especifica y /Zc:twoPhase- , el compilador permite este código sin la template palabra clave . Interpreta el código como una llamada a una plantilla de función con un argumento de a || b, porque solo analiza las plantillas de forma limitada. El código anterior no se analiza en absoluto en la primera fase. Durante la segunda fase, hay contexto suficiente para indicar que T::Foo es una plantilla en lugar de una variable, por lo que el compilador no aplica el uso de la palabra clave.

Este comportamiento también se puede ver al eliminar la palabra clave typename delante de los nombres en los cuerpos de plantilla de función, inicializadores, argumentos predeterminados y argumentos noexcept. Por ejemplo:

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

Si no usa la palabra clave typename en el cuerpo de la función, este código se compila en /permissive- /Zc:twoPhase-, pero no en /permissive- solo. La palabra clave typename es necesaria para indicar que TYPE es dependiente. Dado que el cuerpo no se analiza en /Zc:twoPhase-, el compilador no requiere la palabra clave . En /permissive- el modo de conformidad, el código sin la typename palabra clave genera errores. Para migrar el código de conformidad con Visual Studio 2017 versión 15.3 y posteriores, inserte la palabra clave typename allí donde falte.

Del mismo modo, considere este ejemplo de código:

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

En /permissive- /Zc:twoPhase- y en compiladores anteriores, el compilador solo requiere la palabra clave en la template línea 2. En el modo de conformidad, ahora el compilador también requiere la palabra clave template en la línea 4 para indicar que T::X<T> es una plantilla. Busque el código donde falte esta palabra clave y agréguela para ajustarlo al estándar.

Para obtener más información sobre los problemas de conformidad, consulte Mejoras de conformidad de C++ en Visual Studio y Comportamiento no estándar.

Para establecer esta opción del compilador en el entorno de desarrollo de Visual Studio

  1. Abra el cuadro de diálogo Páginas de propiedades del proyecto. Para más información, vea Establecimiento del compilador de C++ y de propiedades de compilación en Visual Studio.

  2. Seleccione la página de propiedades Propiedades de configuración>C/C++>Línea de comandos.

  3. Modifique la propiedad Opciones adicionales para incluir /Zc:twoPhase- y, a continuación, elija Aceptar.

Consulte también

/Zc (Conformidad)