Partilhar via


/Zc:twoPhase- (desativar a pesquisa de nome em duas fases)

A /Zc:twoPhase- opção, em /permissive-, diz ao compilador para usar o comportamento original do compilador Microsoft C++ não conforme para analisar e instanciar modelos de classe e modelos de função.

Sintaxe

/Zc:twoPhase-

Comentários

Visual Studio 2017 versão 15.3 e posterior: em /permissive-, o compilador usa pesquisa de nome de duas fases para resolução de nome de modelo. Se você também especificar /Zc:twoPhase-, o compilador reverterá para seu modelo de classe não conforme anterior e resolução de nome de função e comportamento de substituição. Quando /permissive- não é especificado, o comportamento de não conformidade é o padrão.

Os arquivos de cabeçalho do SDK do Windows na versão 10.0.15063.0 (Atualização de Criadores ou RS2) e anteriores não funcionam no modo de conformidade. /Zc:twoPhase- é necessário para compilar código para essas versões do SDK quando você usa /permissive-o . As versões do SDK do Windows a partir da versão 10.0.15254.0 (Fall Creators Update ou RS3) funcionam corretamente no modo de conformidade. Eles não exigem a /Zc:twoPhase- opção.

Use /Zc:twoPhase- se o código exigir que o comportamento antigo seja compilado corretamente. Considere fortemente atualizar seu código para estar em conformidade com o padrão.

Comportamento do compilador em /Zc:twoPhase-

Por padrão, ou no Visual Studio 2017 versão 15.3 e posterior quando você especificar ambos /permissive- e /Zc:twoPhase-, o compilador usa este comportamento:

  • Ele analisa apenas a declaração de modelo, o cabeçalho da classe e a lista de classes base. O corpo do modelo é capturado como um fluxo de token. Nenhum corpo de função, inicializadores, argumentos padrão ou argumentos noexcept são analisados. O modelo de classe é pseudoinstanciado em um tipo provisório para validar se as declarações no modelo de classe estão corretas. Considere este modelo de classe:

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

    A declaração de modelo, template <typename T>o cabeçalho da classe class Derived e a lista de classe base public Base<T> são analisados, mas o corpo do modelo é capturado como um fluxo de token.

  • Quando analisa um modelo de função, o compilador analisa apenas a assinatura da função. O corpo da função nunca é analisado. Em vez disso, ele é capturado como um fluxo de token.

Como resultado, se o corpo do modelo tiver erros de sintaxe, mas o modelo nunca for instanciado, o compilador não diagnosticará os erros.

Outro efeito desse comportamento está na resolução de sobrecarga. O comportamento não padrão ocorre devido à maneira como o fluxo de token é expandido no site de instanciação. Símbolos que não estavam visíveis na declaração de modelo podem estar visíveis no ponto de instanciação. Isso significa que eles podem participar da resolução de sobrecarga. Você pode encontrar o comportamento de alteração de modelos com base no código que não estava visível na definição do modelo, ao contrário do padrão.

Por exemplo, 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);
}

Aqui está a saída quando você usa o modo padrão, o modo de conformidade e o modo de conformidade com /Zc:twoPhase- as opções do 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

Quando compilado no modo de conformidade em /permissive-, este programa imprime "Standard two-phase", porque a segunda sobrecarga de func não é visível quando o compilador atinge o modelo. Se você adicionar /Zc:twoPhase-, o programa imprime "Microsoft one-phase". A saída é a mesma de quando você não especifica /permissive-.

Nomes dependentes são nomes que dependem de um parâmetro de modelo. Esses nomes têm comportamento de pesquisa que também é diferente em /Zc:twoPhase-. No modo de conformidade, os nomes dependentes não são associados no ponto da definição do modelo. Em vez disso, o compilador os procura quando cria uma instância do modelo. Para chamadas de função com um nome de função dependente, o nome fica associado a funções visíveis no site de chamada na definição do modelo. Outras sobrecargas da pesquisa dependente de argumento são adicionadas, tanto no ponto da definição do modelo quanto no ponto da instanciação do modelo.

A pesquisa em duas fases consiste em duas partes: a pesquisa de nomes não dependentes durante a definição do modelo e a pesquisa de nomes dependentes durante a instanciação do modelo. Em /Zc:twoPhase-, o compilador não faz pesquisa dependente de argumento separadamente da pesquisa não qualificada. Ou seja, ele não faz pesquisa em duas fases, portanto, os resultados da resolução de sobrecarga podem ser diferentes.

Veja outro exemplo:

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

Quando compilado sem /permissive-, este código imprime:

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

Quando compilado com /permissive-, mas sem /Zc:twoPhase-, este código imprime:

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

Quando compilado com ambos /permissive- e /Zc:twoPhase-, este código imprime:

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

No modo de conformidade em /permissive-, a chamada tfunc(1729) resolve a void func(long) sobrecarga. Não resolve a void func(int) sobrecarga, como em /Zc:twoPhase-. O motivo é que o não qualificado func(int) é declarado após a definição do modelo e não é encontrado por meio de pesquisa dependente de argumento. Mas void func(S) participa da pesquisa dependente de argumento, portanto, ela é adicionada ao conjunto de sobrecarga para a chamada tfunc(s), mesmo que ela seja declarada após o modelo de função.

Atualizar seu código para conformidade em duas fases

As versões mais antigas do compilador não exigem as palavras-chave template e typename em todos os lugares em que o C++ Standard as exige. Essas palavras-chave são necessárias em algumas posições para desambiguar como os compiladores devem analisar um nome dependente durante a primeira fase da pesquisa. Por exemplo:

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

Um compilador em conformidade analisa Foo como uma variável no escopo de T, o que significa que esse código é uma expressão lógica ou com T::foo < a o operando à esquerda e b > (c) como o operando à direita. Se você pretende usar Foo como um modelo de função, deve indicar que ele é um modelo adicionando a template palavra-chave:

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

Nas versões do Visual Studio 2017 versão 15.3 e posterior, quando /permissive- e /Zc:twoPhase- são especificados, o compilador permite esse código sem a template palavra-chave. Ele interpreta o código como uma chamada para um modelo de função com um argumento de a || b, porque ele analisa apenas modelos de forma limitada. O código acima não é analisado na primeira fase. Durante a segunda fase, há contexto suficiente para dizer que T::Foo é um modelo em vez de uma variável, para que o compilador não imponha o uso da palavra-chave.

Esse comportamento também pode ser visto eliminando a palavra-chave typename antes de nomes em corpos de modelo de função, inicializadores, argumentos padrão e argumentos noexcept. Por exemplo:

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

Se você não usar a palavra-chave typename no corpo da função, esse código será compilado em , mas não em /permissive- /Zc:twoPhase-/permissive- sozinho. A palavra-chave typename é necessária para indicar que TYPE é dependente. Como o corpo não é analisado em /Zc:twoPhase-, o compilador não requer a palavra-chave. No /permissive- modo de conformidade, o código sem a typename palavra-chave gera erros. Para migrar seu código para conformidade no Visual Studio 2017 versão 15.3 e posterior, insira a palavra-chave typename em que ele está ausente.

Da mesma forma, considere este exemplo de código:

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

Em /permissive- /Zc:twoPhase- e em compiladores mais antigos, o compilador requer apenas a template palavra-chave na linha 2. No modo de conformidade, o compilador agora também requer a palavra-chave template na linha 4 para indicar que T::X<T> é um modelo. Procure o código que não tem essa palavra-chave e forneça-a para fazer com que o código esteja em conformidade com o padrão.

Para obter mais informações sobre problemas de conformidade, consulte Aprimoramentos de conformidade C++ no Visual Studio e Comportamento não padrão.

Para definir esta opção do compilador no ambiente de desenvolvimento do Visual Studio

  1. Abra a caixa de diálogo Páginas de Propriedades do projeto. Para obter detalhes, confira Definir as propriedades de build e do compilador do C++ no Visual Studio.

  2. Selecione a página de propriedades Propriedades de Configuração>C/C++>Linha de Comando.

  3. Modifique a propriedade Opções Adicionais para incluir /Zc:twoPhase- e selecione OK.

Confira também

/Zc (Conformidade)