Dela via


/Zc:ternary (Framtvinga regler för villkorsstyrd operatör)

Aktivera tillämpning av C++ Standard-regler för typer och nackdelar eller flyktiga (cv) kvalificering av andra och tredje operander i ett villkorsoperatoruttryck.

Syntax

/Zc:ternary[-]

Anmärkningar

Från och med Visual Studio 2017 stöder kompilatorn C++ standardbeteende för villkorsstyrd operator (?:). Det kallas även för ternary-operatorn. C++ Standard kräver att ternaryopernder uppfyller något av tre villkor: Operanderna måste vara av samma typ och const eller volatile kvalificering (cv-kvalificering), eller bara en operand måste vara otvetydigt konvertibel till samma typ och cv-kvalificering som den andra. Eller så måste en eller båda operanderna vara ett utkastsuttryck. I versioner före Visual Studio 2017 version 15.5 tillät kompilatorn konverteringar som anses tvetydiga av standarden.

När alternativet /Zc:ternary har angetts följer kompilatorn standarden. Den avvisar kod som inte uppfyller reglerna för matchade typer och cv-kvalificering för de andra och tredje operanderna.

Alternativet /Zc:ternary är inaktiverat som standard i Visual Studio 2017. Använd /Zc:ternary för att aktivera överensstämmelsebeteende eller /Zc:ternary- för att uttryckligen ange det tidigare icke-överensstämmande kompilatorbeteendet. Alternativet /permissive- aktiverar implicit det här alternativet, men det kan åsidosättas med hjälp /Zc:ternary-av .

Exempel

Det här exemplet visar hur en klass som ger både icke-explicit initiering från en typ och konvertering till en typ kan leda till tvetydiga konverteringar. Den här koden godkänns som standard av kompilatorn, men avvisas när /Zc:ternary eller /permissive- anges.

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

Åtgärda den här koden genom att göra en explicit gjutning till önskad gemensam typ eller förhindra en riktning av typen konvertering. Du kan hindra kompilatorn från att matcha en typkonvertering genom att göra konverteringen explicit.

Ett viktigt undantag till det här vanliga mönstret är när typen av operander är en av de null-avslutade strängtyperna, till exempel const char*, const char16_t*och så vidare. Du kan också återskapa effekten med matristyper och pekartyper som de förfaller till. Beteendet när den faktiska andra eller tredje operanden till ?: är en strängliteral av motsvarande typ beror på vilken språkstandard som används. C++17 har ändrat semantik för det här fallet från C++14. Därför accepterar kompilatorn koden i följande exempel under standardvärdet /std:c++14, men avvisar den när du anger /std:c++17 eller senare.

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

Åtgärda den här koden genom att uttryckligen kasta en av operanderna.

Under /Zc:ternaryavvisar kompilatorn villkorsstyrda operatorer där ett av argumenten är av typen void, och det andra inte är ett throw uttryck. En vanlig användning av det här mönstret finns i ASSERT-liknande makron:

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

Den vanliga lösningen är att ersätta argumentet non-void med void().

Det här exemplet visar kod som genererar ett fel under både /Zc:ternary och /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
}

Den här koden gav tidigare det här felet:

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

Med /Zc:ternaryblir orsaken till felet tydligare. Någon av flera implementeringsdefinierade anropskonventioner kan användas för att generera varje lambda. Kompilatorn har dock ingen inställningsregel för att skilja möjliga lambda-signaturer åt. De nya utdata ser ut så här:

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>)'

En vanlig källa till problem som hittas av /Zc:ternary kommer från villkorsstyrda operatorer som används i mallmetaprogrammering. Vissa resultattyper ändras under den här växeln. I följande exempel visas två fall där /Zc:ternary ändringar av ett villkorsuttrycks resultattyp i en icke-metaprogrammeringskontext:

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

Den typiska korrigeringen är att tillämpa ett std::remove_reference drag på resultattypen, där det behövs för att bevara det gamla beteendet.

Mer information om efterlevnadsproblem i Visual C++finns i Icke-standardbeteende.

Ange det här kompilatoralternativet i Visual Studio-utvecklingsmiljön

  1. Öppna dialogrutan egenskapssidor för projektet. Mer information finns i Ange C++-kompilator- och byggegenskaper i Visual Studio.

  2. Välj egenskapssidan Konfigurationsegenskaper>C/C++>kommandorad.

  3. Ändra egenskapen Ytterligare alternativ för att inkludera /Zc:ternary eller /Zc:ternary- och välj sedan OK.

Se även

/Zc (överensstämmelse)