Partilhar via


/Zc:ternary (Impor regras condicionais do operador)

Habilite a imposição de regras padrão C++ para os tipos e qualificação const ou volátil (cv) do segundo e terceiro operandos em uma expressão de operador condicional.

Sintaxe

/Zc:ternary[-]

Observações

A partir do Visual Studio 2017, o compilador oferece suporte ao comportamento do operador condicional padrão C++ (?:). É também conhecido como operador ternário. A Norma C++ exige que os operandos ternários satisfaçam uma de três condições: Os operandos devem ser do mesmo tipo e const /ou volatile qualificação (cv-qualification), ou apenas um operando deve ser inequivocamente convertível para o mesmo tipo e cv-qualificação que o outro. Ou, um ou ambos os operandos devem ser uma expressão de lançamento. Em versões anteriores ao Visual Studio 2017 versão 15.5, o compilador permitia conversões consideradas ambíguas pelo padrão.

Quando a /Zc:ternary opção é especificada, o compilador está em conformidade com o padrão. Ele rejeita o código que não satisfaz as regras para tipos correspondentes e qualificação cv do segundo e terceiro operandos.

A /Zc:ternary opção 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 do compilador não conforme. A /permissive- opção habilita implicitamente essa opção, mas ela pode ser substituída usando /Zc:ternary-.

Exemplos

Este exemplo mostra como uma classe que fornece inicialização não explícita de um tipo e conversão para 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- é especificado.

// 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 para o tipo comum preferido ou impeça uma direção de conversão de tipo. Você pode impedir que o compilador corresponda a 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 tipos de matriz e os tipos de ponteiro para os quais eles decaem. O comportamento quando o segundo ou terceiro operando real é ?: uma cadeia de caracteres literal do tipo correspondente depende do padrão de linguagem usado. C++17 mudou a semântica para este caso de C++14. Como resultado, o compilador aceita o código no exemplo a seguir sob o padrão /std:c++14, mas o rejeita quando você especifica /std:c++17 ou mais tarde.

// 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, lance um dos operandos explicitamente.

Em /Zc:ternary, o compilador rejeita operadores condicionais onde um dos argumentos é do tipo void, e o outro não é uma throw expressão. Um uso comum desse padrão é em macros do tipo 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 non-void por void().

Este exemplo mostra o código que gera um erro em ambos e /Zc:ternary/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
}

Este código anteriormente deu 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:ternaryo , a razão do fracasso torna-se mais clara. Qualquer uma das várias convenções de chamada definidas pela implementação pode ser usada para gerar cada lambda. No entanto, o compilador não tem nenhuma regra de preferência para desambiguar as possíveis assinaturas 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 modelos. Alguns dos tipos de resultados mudam sob esta 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 de não-metaprogramaçã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 std::remove_reference característica no tipo de resultado, quando necessário para preservar o comportamento antigo.

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

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

  1. Abra a caixa de diálogo Property Pages do projeto. Para obter detalhes, consulte Definir compilador C++ e criar propriedades no Visual Studio.

  2. Selecione a Configuration Properties>C/C++>Command Line página de propriedades.

  3. Modifique a propriedade Opções Adicionais para incluir /Zc:ternary ou /Zc:ternary- escolha OK.

Ver também

/Zc (Conformidade)