/Zc:ternary
(強制執行條件運算符規則)
針對條件運算符表示式中第二個和第三個操作數的類型和常數或揮發性 (cv) 資格,啟用C++標準規則的強制執行。
語法
/Zc:ternary
[-
]
備註
從 Visual Studio 2017 開始,編譯程式支援C++標準 條件運算符 (?:
) 行為。 也稱為三元運算符。 C++標準要求三元操作數符合下列三個條件之一:操作數的類型和 const
或 volatile
資格(cv-qualification),或者只有一個操作數必須明確轉換成與另一種相同的類型和 cv 資格。 或者,一或兩個操作數必須是 throw 運算式。 在 Visual Studio 2017 15.5 版之前的版本,編譯程式允許將標準視為模棱兩可的轉換。
/Zc:ternary
指定 選項時,編譯程式符合標準。 它會拒絕不符合第二和第三個操作數之相符類型和 cv 限定性規則的程式代碼。
在 Visual Studio 2017 中,此選項 /Zc:ternary
預設為關閉。 使用 /Zc:ternary
來啟用符合規範的行為,或 /Zc:ternary-
明確指定先前不符合規範的編譯程序行為。 選項 /permissive-
會隱含地啟用這個選項,但可以使用 來覆寫 /Zc:ternary-
。
範例
此範例示範類別如何同時提供類型的非明確初始化,以及轉換成類型,可能會導致模棱兩可的轉換。 編譯程式預設會接受此程序代碼,但在指定 或 /permissive-
時/Zc:ternary
遭到拒絕。
// 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;
}
若要修正此程式碼,請明確轉換成慣用的一般類型,或防止一個類型轉換方向。 您可以讓轉換明確,讓編譯程式無法比對類型轉換。
此常見模式的重要例外狀況是操作數的類型是其中一個以 Null 終止的字串類型,例如 const char*
、 const char16_t*
等等。 您也可以使用數位類型及其衰變的指標類型來重現效果。 當的實際第二個或第三個操作數 ?:
是對應型別的字串常值時的行為,取決於所使用的語言標準。 C++17 已從 C++14 變更此案例的語意。 因此,編譯程式會接受下列範例中的程序 /std:c++14
代碼,但當您指定 /std:c++17
或更新版本時,會拒絕它。
// 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
}
若要修正此程式碼,請明確轉換其中一個操作數。
在下 /Zc:ternary
,編譯程式會拒絕條件運算元,其中其中一個自變數的類型為 void
,而另一個 throw
則不是表達式。 此模式的常見用法是在類似 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
}
典型的解決方案是將非 void 自變數取代為 void()
。
這個範例示範在和/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
}
此程式代碼先前已提供此錯誤:
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
使用 /Zc:ternary
時,失敗的原因會變得更清楚。 您可以使用數個實作定義的呼叫慣例來產生每個 Lambda。 不過,編譯程式沒有喜好設定規則來釐清可能的 Lambda 簽章。 新的輸出看起來像這樣:
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>)'
找到 /Zc:ternary
的常見問題來源來自範本中繼程式設計中使用的條件運算符。 這個參數下的某些結果類型會變更。 下列範例示範兩種情況,其中 /Zc:ternary
在非中繼程式設計內容中變更條件表達式的結果類型:
// 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;
}
典型的修正方式是在結果類型上套用 std::remove_reference
特性,而需要保留舊的行為。
如需 Visual C++ 中一致性問題的詳細資訊,請參閱 Nonstandard Behavior。
在 Visual Studio 開發環境中設定這個編譯器選項
開啟專案的 [屬性頁] 對話方塊。 如需詳細資料,請參閱在 Visual Studio 中設定 C ++ 編譯器和組建屬性。
選取 [組態屬性]>[C/C++]>[命令列] 屬性頁。
修改 [其他選項] 屬性以包含
/Zc:ternary
或/Zc:ternary-
,然後選擇 [確定]。