/Zc:ternary
(Regeln für bedingten Operator erzwingen)
Aktivieren Sie die Erzwingung von C++-Standardregeln für die Typen und konstanten oder veränderlichen (cv) Qualifikation der zweiten und dritten Operanden in einem Bedingten Operatorausdruck.
Syntax
/Zc:ternary
[-
]
Hinweise
Ab Visual Studio 2017 unterstützt der Compiler das Verhalten des bedingten C++-Standardoperators (?:
). Es wird auch als ternärer Operator bezeichnet. Der C++-Standard erfordert ternäre Operanden, die eine von drei Bedingungen erfüllen: Die Operanden müssen denselben Typ und const
oder volatile
die gleiche Qualifikation (Cv-Qualifikation) aufweisen, oder nur ein Operand muss eindeutig in denselben Typ und denselben Cv-Qualifikation wie der andere konvertierbar sein. Oder ein oder beide Operanden müssen ein Auslösenausdruck sein. In Versionen vor Visual Studio 2017, Version 15.5, erlaubte der Compiler Konvertierungen, die standardmäßig als mehrdeutig angesehen werden.
Wenn die Option angegeben wird, entspricht der /Zc:ternary
Compiler dem Standard. Sie lehnt Code ab, der die Regeln für übereinstimmende Typen und cv-Qualifikation der zweiten und dritten Operanden nicht erfüllt.
Die /Zc:ternary
Option ist in Visual Studio 2017 standardmäßig deaktiviert. Dient /Zc:ternary
zum Aktivieren des konformen Verhaltens oder /Zc:ternary-
zum expliziten Angeben des vorherigen nicht konformen Compilerverhaltens. Die Option aktiviert diese Option implizit, kann jedoch mithilfe /Zc:ternary-
von .<
Beispiele
In diesem Beispiel wird gezeigt, wie eine Klasse, die sowohl eine nicht explizite Initialisierung von einem Typ als auch eine Konvertierung in einen Typ bereitstellt, zu mehrdeutigen Konvertierungen führen kann. Dieser Code wird standardmäßig vom Compiler akzeptiert, wird jedoch abgelehnt, wenn /Zc:ternary
oder /permissive-
angegeben wird.
// 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;
}
Um diesen Code zu beheben, erstellen Sie eine explizite Umwandlung in den bevorzugten allgemeinen Typ, oder verhindern Sie eine Richtung der Typkonvertierung. Sie können den Compiler daran hindern, eine Typkonvertierung zuzuordnen, indem Sie die Konvertierung explizit vornehmen.
Eine wichtige Ausnahme zu diesem allgemeinen Muster ist, wenn der Typ der Operanden eines der null-beendeten Zeichenfolgentypen ist, z const char*
. B. , const char16_t*
usw. Sie können den Effekt auch mit Arraytypen und den Zeigertypen reproduzieren, in die sie verfallen. Das Verhalten, wenn der tatsächliche zweite oder dritte Operand ?:
ein Zeichenfolgenliteral des entsprechenden Typs ist, hängt von dem verwendeten Sprachstandard ab. C++17 hat die Semantik für diesen Fall von C++14 geändert. Daher akzeptiert der Compiler den Code im folgenden Beispiel unter der Standardeinstellung /std:c++14
, lehnt ihn jedoch ab, wenn Sie angeben /std:c++17
oder höher.
// 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
}
Um diesen Code zu beheben, wandeln Sie einen der Operanden explizit um.
Under /Zc:ternary
, the compiler rejects conditional operators where one of the arguments is of type void
, and the other isn't a throw
expression. Eine häufige Verwendung dieses Musters ist in ASSERT-ähnlichen Makros:
// 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
}
Die typische Lösung besteht darin, das nicht leere Argument durch void()
.
Dieses Beispiel zeigt Code, der einen Fehler unter beiden /Zc:ternary
und /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
}
Dieser Code hat zuvor diesen Fehler ausgegeben:
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
Mit /Zc:ternary
, der Grund für fehler wird klarer. Jede von mehreren implementierungsdefinierte Aufrufkonventionen kann verwendet werden, um jede Lambda-Funktion zu generieren. Der Compiler hat jedoch keine Einstellungsregel, um die möglichen Lambda-Signaturen zu unterscheiden. Die neue Ausgabe sieht wie folgt aus:
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>)'
Eine häufige Quelle von Problemen, die von /Zc:ternary
bedingten Operatoren gefunden wurden, die in der Vorlagenmetaprogrammierung verwendet werden. Einige der Ergebnistypen ändern sich unter dieser Option. Im folgenden Beispiel werden zwei Fälle veranschaulicht, in denen /Zc:ternary
der Ergebnistyp eines bedingten Ausdrucks in einem Kontext ohne Metaprogrammierung geändert wird:
// 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;
}
Der typische Fix besteht darin, eine std::remove_reference
Eigenschaft auf den Ergebnistyp anzuwenden, sofern erforderlich, um das alte Verhalten beizubehalten.
Weitere Informationen über Konformitätsprobleme in Visual C++ finden Sie unter Nonstandard Behavior.
So legen Sie diese Compileroption in der Visual Studio-Entwicklungsumgebung fest
Öffnen Sie das Dialogfeld Eigenschaftenseiten des Projekts. Weitere Informationen erhalten Sie unter Set C++ compiler and build properties in Visual Studio (Festlegen der Compiler- und Buildeigenschaften (C++) in Visual Studio).
Klicken Sie auf der Eigenschaftenseite auf Konfigurationseigenschaften>C/C++>Befehlszeile.
Ändern Sie die Eigenschaft "Zusätzliche Optionen", um sie einzuschließen
/Zc:ternary
oder/Zc:ternary-
auszuwählen, und wählen Sie dann "OK" aus.