Sdílet prostřednictvím


/Zc:ternary (Vynucení pravidel podmíněného operátoru)

Povolte vynucení standardních pravidel jazyka C++ pro typy a konstanční nebo nestálé (cv) kvalifikace druhého a třetího operandu ve výrazu podmíněného operátoru.

Syntaxe

/Zc:ternary[-]

Poznámky

Počínaje sadou Visual Studio 2017 kompilátor podporuje chování standardního podmíněného operátoru jazyka?: C++. Označuje se také jako ternární operátor. Standard C++ vyžaduje, aby ternární operandy splňovaly jednu ze tří podmínek: Operandy musí být stejného typu a const kvalifikace volatile (cv-kvalifikace) nebo pouze jeden operand musí být jednoznačně konvertibilní na stejný typ a kvalifikaci cv jako druhý. Nebo jeden nebo oba operandy musí být výraz throw. Ve verzích před sadou Visual Studio 2017 verze 15.5 kompilátor povolil převody, které jsou považovány za nejednoznačné podle standardu.

/Zc:ternary Pokud je tato možnost zadaná, kompilátor odpovídá standardu. Odmítne kód, který nesplňuje pravidla pro odpovídající typy a kvalifikaci cv-kvalifikace druhého a třetího operandu.

Tato /Zc:ternary možnost je ve výchozím nastavení v sadě Visual Studio 2017 vypnutá. Slouží /Zc:ternary k povolení odpovídajícího chování nebo /Zc:ternary- k explicitnímu zadání předchozího nevyhovujícího chování kompilátoru. Možnost /permissive- tuto možnost implicitně povolí, ale lze ji přepsat pomocí ./Zc:ternary-

Příklady

Tato ukázka ukazuje, jak třída, která poskytuje explicitní inicializaci typu a převod na typ, může vést k nejednoznačným převodům. Tento kód je ve výchozím nastavení přijat kompilátorem, ale odmítnut, pokud /Zc:ternary je zadán nebo /permissive- je zadán.

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

Chcete-li tento kód opravit, proveďte explicitní přetypování na upřednostňovaný běžný typ nebo zabránit jednomu směru převodu typů. Kompilátor můžete zachovat v porovnání převodu typu tak, že převod explicitně provedete.

Důležitou výjimkou tohoto běžného vzoru je, že typ operandů je jedním z typů řetězců ukončených hodnotou null, například const char*, const char16_t*atd. Efekt můžete také reprodukovat pomocí typů polí a typů ukazatelů, na které se rozpadají. Chování, když skutečný druhý nebo třetí operand je ?: řetězcový literál odpovídajícího typu závisí na použitém jazykovém standardu. C++17 změnil sémantiku pro tento případ z C++14. V důsledku toho kompilátor přijme kód v následujícím příkladu ve výchozím nastavení /std:c++14, ale odmítne ho při zadání /std:c++17 nebo později.

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

Pokud chcete tento kód opravit, přetypujte jeden z operandů explicitně.

V části /Zc:ternarykompilátor odmítne podmíněné operátory, kde jeden z argumentů je typu voida druhý není throw výraz. Běžné použití tohoto modelu je v makrech podobných 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
}

Typickým řešením je nahradit argument void(), který není void .

Tato ukázka ukazuje kód, který vygeneruje chybu v obou /Zc:ternary případech:/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
}

Tento kód dříve dal tuto chybu:

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

S /Zc:ternary, důvod selhání je jasnější. Ke generování každé lambdy je možné použít některou z konvencí definovaných implementací. Kompilátor ale nemá žádné pravidlo předvoleb k nejednoznačnosti možných podpisů lambda. Nový výstup vypadá takto:

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

Běžným zdrojem zjištěných /Zc:ternary problémů jsou podmíněné operátory používané při metaprogramování šablony. Některé typy výsledků se v tomto přepínači mění. Následující příklad ukazuje dva případy, kdy /Zc:ternary změní typ výsledku podmíněného výrazu v kontextu jiného než metaprogramu:

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

Typická std::remove_reference oprava spočívá v použití vlastnosti u typu výsledku, kde je potřeba zachovat staré chování.

Další informace o problémech s shodami v jazyce Visual C++ naleznete v tématu Nestandardní chování.

Nastavení tohoto parametru kompilátoru ve vývojovém prostředí Visual Studio

  1. Otevřete dialogové okno Stránky vlastností projektu. Podrobnosti najdete v tématu Nastavení kompilátoru C++ a vlastností sestavení v sadě Visual Studio.

  2. Vyberte stránku vlastností příkazového řádku C/C++>Vlastnosti>konfigurace.

  3. Upravte vlastnost Další možnosti tak, aby zahrnovala /Zc:ternary nebo /Zc:ternary- a pak zvolte OK.

Viz také

/Zc (Shoda)