Megosztás a következőn keresztül:


/Zc:ternary (Feltételes operátorszabályok kényszerítése)

A C++ Standard szabályok érvényesítésének engedélyezése a második és harmadik operandus típusára és const vagy volatilis (cv) minősítésére egy feltételes operátorkifejezésben.

Szemantika

/Zc:ternary[]-

Megjegyzések

A Visual Studio 2017-től kezdve a fordító támogatja a C++ standard feltételes operátor (?:) viselkedését. Ternáris operátorként is ismert. A C++ Standard megköveteli, hogy a ternáris operandusok megfeleljenek a három feltétel egyikének: Az operandusoknak azonos típusúnak és const képesítésűnek volatile kell lenniük (cv-minősítés), vagy csak az egyik operandus legyen egyértelműen átalakítható ugyanarra a típusra és a cv-minősítésre, mint a másikra. Vagy egy vagy mindkét operandusnak dobókifejezésnek kell lennie. A Visual Studio 2017 15.5-ös verziója előtti verziókban a fordító olyan átalakításokat engedélyezett, amelyeket a szabvány nem egyértelműnek tart.

Ha a /Zc:ternary beállítás meg van adva, a fordító megfelel a szabványnak. Elutasítja azokat a kódot, amelyek nem felelnek meg a második és harmadik operandusok megfeleltetésének és cv-minősítésének.

A /Zc:ternary beállítás alapértelmezés szerint ki van kapcsolva a Visual Studio 2017-ben. A megfelelő viselkedés engedélyezésére vagy /Zc:ternary a korábbi nem megfelelő fordítói viselkedés explicit megadására használható/Zc:ternary-. A /permissive- beállítás implicit módon engedélyezi ezt a beállítást, de felül lehet bírálni a használatával /Zc:ternary-.

Példák

Ez a minta azt mutatja be, hogy egy olyan osztály, amely nem explicit inicializálást biztosít egy típusból, és hogyan alakít át típusra, nem egyértelmű konverziókhoz vezethet. Ezt a kódot a fordító alapértelmezés szerint elfogadja, de a megadott vagy megadott esetben /Zc:ternary/permissive- elutasítja.

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

A kód javításához hozzon létre egy explicit öntöttet az előnyben részesített általános típusra, vagy tiltsa le a típuskonverzió egyik irányát. A fordítás explicitvá tételével megtarthatja, hogy a fordító megfeleltethető legyen a típuskonverziónak.

Fontos kivétel ez alól a gyakori minta, ha az operandusok típusa az egyik null értékű sztringtípus, például const char*, const char16_t*és így tovább. Az effektust tömbtípusokkal és az általuk használt mutatótípusokkal is reprodukálhatja. Az a viselkedés, amikor a tényleges második vagy harmadik operandus ?: egy megfelelő típusú sztringkonstans, a használt nyelvi szabványtól függ. A C++17 módosította az eset szemantikáját a C++14-ről. Ennek eredményeképpen a fordító elfogadja a következő példában szereplő kódot az alapértelmezett beállításnál /std:c++14, de a megadott /std:c++17 vagy újabb beállításkor elutasítja azt.

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

A kód javításához az egyik operandust explicit módon kell leadnia.

Alatta /Zc:ternarya fordító elutasítja azokat a feltételes operátorokat, amelyekben az egyik argumentum típusú void, a másik pedig nem throw kifejezés. Ennek a mintának a gyakori használata az ASSERT-szerű makrókban van:

// 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 tipikus megoldás a nem érvénytelen argumentum lecserélése a következőre void(): .

Ez a minta olyan kódot mutat be, amely hibát generál a következő és /Zc:ternarya következő /Zc:ternary- alatt:

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

Ez a kód korábban a következő hibát adta:

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

Ezzel /Zc:ternarya hibával a hiba oka egyértelműbbé válik. Az egyes lambdák létrehozásához számos implementáció által definiált hívási konvenció használható. A fordító azonban nem rendelkezik olyan preferenciális szabállyal, amely egyértelműsíti a lehetséges lambda-aláírásokat. Az új kimenet a következőképpen néz ki:

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

A problémák /Zc:ternary gyakori forrása a sablon metaprogramozásában használt feltételes operátorok. A kapcsoló alatt az eredménytípusok némelyike megváltozik. Az alábbi példa két esetet mutat be, amikor /Zc:ternary egy feltételes kifejezés eredménytípusát nem metaprogramozási környezetben módosítja:

// 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 tipikus javítás egy tulajdonság alkalmazása std::remove_reference az eredménytípusra, ahol szükség van a régi viselkedés megőrzésére.

A Visual C++ kompatibilitási problémáival kapcsolatos további információkért lásd: Nem megfelelő viselkedés.

A fordító beállításának beállítása a Visual Studio fejlesztői környezetében

  1. Nyissa meg a projekt Tulajdonságlapok párbeszédpanelt. További információ: C++ fordító és buildtulajdonságok beállítása a Visual Studio.

  2. Válassza a Konfiguráció tulajdonságai>C/C++>parancssori tulajdonságlapját.

  3. Módosítsa a További beállítások tulajdonságot a belefoglaláshoz /Zc:ternary , majd /Zc:ternary- válassza az OK gombot.

Lásd még

/Zc (megfelelőség)