Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
A /Zc:twoPhase-
opção, em /permissive-
, diz ao compilador para usar o comportamento original e não conforme do compilador Microsoft C++ para analisar e instanciar modelos de classe e modelos de função.
Sintaxe
/Zc:twoPhase-
Observações
Visual Studio 2017 versão 15.3 e posterior: em /permissive-
, o compilador usa pesquisa de nome em 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 modelo de função e comportamento de substituição. Quando /permissive-
não é especificado, o comportamento não conforme é o padrão.
Os arquivos de cabeçalho do SDK do Windows na versão 10.0.15063.0 (Creators Update ou RS2) e anteriores não funcionam no modo de conformidade.
/Zc:twoPhase-
é necessário 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 seu código requer o comportamento antigo para compilar 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ê especifica ambos /permissive-
e /Zc:twoPhase-
, o compilador usa esse 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 sem exceção são analisados. O modelo de classe é pseudo-instanciado 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çalhoclass Derived
da classe e a listapublic Base<T>
de classe base 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, é 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 é na resolução de sobrecarga. O comportamento não padrão ocorre devido à maneira como o fluxo de token é expandido no local da instanciação. Os 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 sobrecargas. Você pode achar que os modelos mudam o comportamento com base no código que não era 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 um comportamento de pesquisa que também é diferente em /Zc:twoPhase-
. No modo de conformidade, os nomes dependentes não são vinculados no ponto de definição do modelo. Em vez disso, o compilador os procura quando instancia o modelo. Para chamadas de função com um nome de função dependente, o nome fica vinculado a funções visíveis no site de chamada na definição de 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, então os resultados da resolução de sobrecarga podem ser diferentes.
Eis 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)
é resolvida para a void func(long)
sobrecarga. Ele não resolve para 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, por isso é adicionado ao conjunto de sobrecarga para a chamada tfunc(s)
, mesmo que seja declarado após o modelo de função.
Atualize seu código para conformidade bifásica
Versões mais antigas do compilador não exigem as palavras-chave template
e typename
em todos os lugares 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 de 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 este código é uma expressão lógica ou com T::foo < a
como o operando esquerdo e b > (c)
como o operando direito. Se você pretende usar Foo
como um modelo de função, você deve indicar que é 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 posteriores, quando /permissive-
e /Zc:twoPhase-
são especificadas, o compilador permite esse código sem a palavra-chave template
. 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 é um modelo em vez de uma variável, para que T::Foo
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 /permissive- /Zc:twoPhase-
, mas não sozinho /permissive-
. A typename
palavra-chave é necessária para indicar que o 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 typename
palavra-chave onde ela está faltando.
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;
}
Sob /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 template
palavra-chave na linha 4 para indicar que T::X<T>
é um modelo. Procure o código que está faltando essa palavra-chave e forneça-o para tornar seu código compatível 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 essa opção de compilador no ambiente de desenvolvimento do Visual Studio
Abra a caixa de diálogo Property Pages do projeto. Para obter detalhes, consulte Definir compilador C++ e criar propriedades no Visual Studio.
Selecione a Configuration Properties>C/C++>Command Line página de propriedades.
Modifique a propriedade Opções Adicionais para incluir
/Zc:twoPhase-
e escolha OK.