/Zc:ternary (Applicare le regole dell'operatore condizionale)

Abilitare l'applicazione delle regole standard C++ per i tipi e la qualificazione const o volatile (cv) del secondo e terzo operando in un'espressione di operatore condizionale.

Sintassi

/Zc:ternary[-]

Osservazioni:

A partire da Visual Studio 2017, il compilatore supporta il comportamento dell'operatore condizionale standard (?:) C++. È noto anche come operatore ternario. Lo standard C++ richiede che gli operandi ternari soddisfino una di tre condizioni: gli operandi devono essere dello stesso tipo e const o volatile qualifica (cv-qualification) oppure un solo operando deve essere convertibile in modo non ambiguo nello stesso tipo e qualifica cv dell'altro. In alternativa, uno o entrambi gli operandi devono essere un'espressione throw. Nelle versioni precedenti a Visual Studio 2017 versione 15.5, il compilatore ha consentito conversioni considerate ambigue dallo standard.

Quando si specifica l'opzione /Zc:ternary , il compilatore è conforme allo standard. Rifiuta il codice che non soddisfa le regole per i tipi corrispondenti e la qualificazione cv del secondo e del terzo operando.

L'opzione /Zc:ternary è disattivata per impostazione predefinita in Visual Studio 2017. Usare /Zc:ternary per abilitare il comportamento conforme o /Zc:ternary- specificare in modo esplicito il comportamento precedente del compilatore non conforme. L'opzione /permissive- abilita in modo implicito questa opzione, ma può essere sottoposta a override tramite /Zc:ternary-.

Esempi

In questo esempio viene illustrato come una classe che fornisce sia l'inizializzazione non esplicita da un tipo che la conversione a un tipo può causare conversioni ambigue. Questo codice viene accettato dal compilatore per impostazione predefinita, ma rifiutato quando /Zc:ternary viene specificato o /permissive- .

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

Per correggere questo codice, impostare un cast esplicito sul tipo comune preferito o impedire una direzione di conversione del tipo. È possibile evitare che il compilatore corrisponda a una conversione di tipo rendendo esplicita la conversione.

Un'eccezione importante a questo modello comune è quando il tipo degli operandi è uno dei tipi di stringa con terminazione Null, ad esempio const char*, const char16_t*e così via. È anche possibile riprodurre l'effetto con i tipi di matrice e i tipi di puntatore a cui decadono. Il comportamento quando il secondo o il terzo operando effettivo in ?: è un valore letterale stringa del tipo corrispondente dipende dallo standard linguistico usato. C++17 ha modificato la semantica per questo caso da C++14. Di conseguenza, il compilatore accetta il codice nell'esempio seguente sotto l'impostazione predefinita /std:c++14, ma lo rifiuta quando si specifica /std:c++17 o versioni successive.

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

Per correggere questo codice, eseguire il cast di uno degli operandi in modo esplicito.

In /Zc:ternaryil compilatore rifiuta gli operatori condizionali in cui uno degli argomenti è di tipo voide l'altro non è un'espressione throw . Un uso comune di questo modello è nelle macro simili a 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
}

La soluzione tipica consiste nel sostituire l'argomento non void con void().

Questo esempio mostra il codice che genera un errore sia /Zc:ternary in che /Zc:ternary-in :

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

Questo codice in precedenza ha restituito questo errore:

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

Con /Zc:ternary, il motivo dell'errore diventa più chiaro. È possibile usare qualsiasi convenzione di chiamata definita dall'implementazione per generare ogni espressione lambda. Tuttavia, il compilatore non ha alcuna regola di preferenza per disambiguare le possibili firme lambda. Il nuovo output è simile al seguente:

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

Una fonte comune di problemi rilevati da deriva dagli /Zc:ternary operatori condizionali usati nella meta-programmazione del modello. Alcuni tipi di risultati cambiano in questa opzione. L'esempio seguente illustra due casi in cui /Zc:ternary cambia il tipo di risultato di un'espressione condizionale in un contesto di programmazione non meta-programmazione:

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

La correzione tipica consiste nell'applicare un std::remove_reference tratto al tipo di risultato, se necessario per mantenere il comportamento precedente.

Per altre informazioni sui problemi di conformità in Visual C++, vedere Nonstandard Behavior.

Per impostare l'opzione del compilatore nell'ambiente di sviluppo di Visual Studio

  1. Aprire la finestra di dialogo Pagine delle proprietà del progetto. Per informazioni dettagliate, vedere Impostare il compilatore e le proprietà di compilazione.

  2. Selezionare la pagina delle proprietà Proprietà di configurazione>C/C++>Riga di comando.

  3. Modificare la proprietà Opzioni aggiuntive per includere /Zc:ternary o /Zc:ternary- quindi scegliere OK.

Vedi anche

/Zc (Conformità)