Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Habilite a imposição de regras padrão do C++ para os tipos e a qualificação const ou volátil (cv) do segundo e terceiro operandos em uma expressão de operador condicional.
Sintaxe
/Zc:ternary[-]
Comentários
A partir do Visual Studio 2017, o compilador dá suporte ao comportamento do operador condicional (?:) padrão do C++. Ele também é conhecido como o operador ternário. O Padrão do C++ exige que operandos ternários satisfaçam uma dessas três condições: os operandos devem ser do mesmo tipo e ter qualificação const ou volatile (qualificação cv), ou apenas um operando deve ser conversível de maneira não ambígua no mesmo tipo e ter qualificação cv assim como o outro. Ou um ou ambos os operandos devem ser uma expressão throw. Em versões anteriores à 15.5 do Visual Studio 2017, o compilador permitia conversões que eram consideradas ambíguas pelo padrão.
Quando a opção /Zc:ternary é especificada, o compilador se conforma ao padrão. Ele rejeita o código que não atende às regras para tipos correspondentes e a qualificação cv do segundo e terceiro operandos.
A opção /Zc:ternary está desativada por padrão no Visual Studio 2017. Use /Zc:ternary para habilitar o comportamento de conformidade ou /Zc:ternary- para especificar explicitamente o comportamento anterior de não conformidade do compilador. A opção /permissive- habilita implicitamente essa opção, mas pode ser substituída usando /Zc:ternary-.
Exemplos
Este exemplo mostra como uma classe que fornece a inicialização não explícita de um tipo e a conversão em um tipo, pode levar a conversões ambíguas. Esse código é aceito pelo compilador por padrão, mas rejeitado quando /Zc:ternary ou /permissive- é especificada.
// zcternary1.cpp
// Compile by using: cl /EHsc /W4 /nologo /Zc:ternary zcternary1.cpp
struct A
{
long l;
A(int i) : l{i} {} // explicit prevents conversion of int
operator int() const { return static_cast<int>(l); }
};
int main()
{
A a(42);
// Accepted when /Zc:ternary (or /permissive-) is not used
auto x = true ? 7 : a; // old behavior prefers A(7) over (int)a
auto y = true ? A(7) : a; // always accepted
auto z = true ? 7 : (int)a; // always accepted
return x + y + z;
}
Para corrigir esse código, faça uma conversão explícita no tipo comum preferencial ou impeça uma direção de conversão de tipo. Você pode impedir que o compilador faça a correspondência de uma conversão de tipo, tornando a conversão explícita.
Uma exceção importante a esse padrão comum é quando o tipo dos operandos é um dos tipos de cadeia de caracteres terminada em nulo, como const char*, const char16_t* e assim por diante. Você também pode reproduzir o efeito com os tipos de matriz e os tipos de ponteiro para os quais eles decaem. O comportamento quando o segundo ou terceiro operando real é ?: um literal de cadeia de caracteres do tipo correspondente, depende do padrão de linguagem usado. O C++17 alterou a semântica para esse caso do C++14. Como resultado, o compilador aceita o código no exemplo a seguir sob o /std:c++14 padrão, mas o rejeita quando você especifica /std:c++17 ou posterior.
// zcternary2.cpp
// Compile by using: cl /EHsc /W4 /nologo /Zc:ternary /std:c++17 zcternary2.cpp
struct MyString
{
const char * p;
MyString(const char* s = "") noexcept : p{s} {} // from char*
operator const char*() const noexcept { return p; } // to char*
};
int main()
{
MyString s;
auto x = true ? "A" : s; // MyString: permissive prefers MyString("A") over (const char*)s
}
Para corrigir esse código, solucione um dos operandos explicitamente.
No /Zc:ternary, o compilador rejeita operadores condicionais em que um dos argumentos é do tipo void e o outro não é uma expressão throw. Um uso comum desse padrão está em macros semelhantes a ASSERT:
// zcternary3.cpp
// Compile by using: cl /EHsc /W4 /nologo /Zc:ternary /c zcternary3.cpp
void myassert(const char* text, const char* file, int line);
#define ASSERT(ex) (void)((ex) ? 0 : myassert(#ex, __FILE__, __LINE__))
// To fix, define it this way instead:
// #define ASSERT(ex) (void)((ex) ? void() : myassert(#ex, __FILE__, __LINE__))
int main()
{
ASSERT(false); // C3447
}
A solução típica é substituir o argumento não nulo por void().
Este exemplo mostra o código que gera um erro em /Zc:ternary e /Zc:ternary-:
// zcternary4.cpp
// Compile by using:
// cl /EHsc /W4 /nologo /Zc:ternary zcternary4.cpp
// cl /EHsc /W4 /nologo /Zc:ternary zcternary4.cpp
int main() {
auto p1 = [](int a, int b) { return a > b; };
auto p2 = [](int a, int b) { return a > b; };
auto p3 = true ? p1 : p2; // C2593 under /Zc:ternary, was C2446
}
Esse código anteriormente produzia este erro:
error C2446: ':': no conversion from 'foo::<lambda_f6cd18702c42f6cd636bfee362b37033>' to 'foo::<lambda_717fca3fc65510deea10bc47e2b06be4>'
note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
Com /Zc:ternary, o motivo da falha torna-se mais claro. Qualquer uma das várias convenções de chamada definidas pela implementação poderia ser usada para gerar cada lambda. No entanto, o compilador não tem nenhuma regra de preferência para desambiguar as possíveis assinaturas de lambda. A nova saída tem esta aparência:
error C2593: 'operator ?' is ambiguous
note: could be 'built-in C++ operator?(bool (__cdecl *)(int,int), bool (__cdecl *)(int,int))'
note: or 'built-in C++ operator?(bool (__stdcall *)(int,int), bool (__stdcall *)(int,int))'
note: or 'built-in C++ operator?(bool (__fastcall *)(int,int), bool (__fastcall *)(int,int))'
note: or 'built-in C++ operator?(bool (__vectorcall *)(int,int), bool (__vectorcall *)(int,int))'
note: while trying to match the argument list '(foo::<lambda_717fca3fc65510deea10bc47e2b06be4>, foo::<lambda_f6cd18702c42f6cd636bfee362b37033>)'
Uma fonte comum de problemas encontrados por /Zc:ternary vem de operadores condicionais usados na meta-programação de modelo. Alguns dos tipos de resultado são alterados nessa opção. O exemplo a seguir demonstra dois casos em que /Zc:ternary altera o tipo de resultado de uma expressão condicional em um contexto diferente de meta-programação:
// zcternary5.cpp
// Compile by using: cl /EHsc /W4 /nologo /Zc:ternary zcternary5.cpp
int main(int argc, char**) {
char a = 'A';
const char b = 'B';
decltype(auto) x = true ? a : b; // char without, const char& with /Zc:ternary
const char(&z)[2] = argc > 3 ? "A" : "B"; // const char* without /Zc:ternary
return x > *z;
}
A correção típica é aplicar uma característica std::remove_reference no tipo de resultado, sempre que for necessário para preservar o comportamento antigo.
Para obter mais informações sobre problemas de conformidade no Visual C++, confira Comportamento fora do padrão.
Para definir esta opção do compilador no ambiente de desenvolvimento do Visual Studio
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.
Selecione a página de propriedades Propriedades de Configuração>C/C++>Linha de Comando.
Modifique a propriedade Opções Adicionais para incluir
/Zc:ternaryou/Zc:ternary-, e escolha OK.