Bagikan melalui


/Zc:ternary (Menerapkan aturan operator bersyar)

Aktifkan penegakan aturan Standar C++ untuk jenis dan kualifikasi const atau volatil (cv) dari operand kedua dan ketiga dalam ekspresi operator bersyarah.

Sintaks

/Zc:ternary[-]

Keterangan

Mulai Visual Studio 2017, pengkompilasi mendukung perilaku operator bersyargi standar (?:) C++. Ini juga dikenal sebagai operator ternary. Standar C++ memerlukan operand terner memenuhi salah satu dari tiga kondisi: Operan harus memiliki jenis dan const atau volatile kualifikasi yang sama (kualifikasi cv), atau hanya satu operan yang harus dikonversi secara tidak ambigu ke jenis dan kualifikasi cv yang sama dengan yang lain. Atau, satu atau kedua operand harus berupa ekspresi lemparan. Dalam versi sebelum Visual Studio 2017 versi 15.5, pengompilasi mengizinkan konversi yang dianggap ambigu oleh standar.

/Zc:ternary Ketika opsi ditentukan, pengkompilasi sesuai dengan standar. Ini menolak kode yang tidak memenuhi aturan untuk jenis yang cocok dan kualifikasi cv dari operand kedua dan ketiga.

Opsi /Zc:ternary nonaktif secara default di Visual Studio 2017. Gunakan /Zc:ternary untuk mengaktifkan perilaku yang sesuai, atau /Zc:ternary- untuk secara eksplisit menentukan perilaku pengkompilasi yang tidak sesuai sebelumnya. Opsi /permissive- ini secara implisit mengaktifkan opsi ini, tetapi dapat ditimpa dengan menggunakan /Zc:ternary-.

Contoh

Sampel ini menunjukkan bagaimana kelas yang menyediakan inisialisasi non-eksplisit dari jenis, dan konversi ke jenis, dapat menyebabkan konversi ambigu. Kode ini diterima oleh pengkompilasi secara default, tetapi ditolak ketika /Zc:ternary atau /permissive- ditentukan.

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

Untuk memperbaiki kode ini, buat cast eksplisit ke jenis umum pilihan, atau cegah satu arah konversi jenis. Anda dapat menjaga pengkompilasi agar tidak cocok dengan konversi jenis dengan membuat konversi menjadi eksplisit.

Pengecualian penting untuk pola umum ini adalah ketika jenis operand adalah salah satu jenis string yang dihentikan null, seperti , const char16_t*, dan sebagainyaconst char*. Anda juga dapat mereproduksi efek dengan jenis array dan jenis penunjuk yang di-decay. Perilaku ketika operand ?: kedua atau ketiga yang sebenarnya adalah string harfiah dari jenis yang sesuai tergantung pada standar bahasa yang digunakan. C++17 telah mengubah semantik untuk kasus ini dari C++14. Akibatnya, pengkompilasi menerima kode dalam contoh berikut di bawah default /std:c++14, tetapi menolaknya saat Anda menentukan /std:c++17 atau nanti.

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

Untuk memperbaiki kode ini, transmisikan salah satu operan secara eksplisit.

Di bawah /Zc:ternary, pengkompilasi menolak operator bersyarkat di mana salah satu argumen berjenis void, dan yang lain bukan throw ekspresi. Penggunaan umum pola ini ada di makro seperti 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
}

Solusi umumnya adalah mengganti argumen yang tidak batal dengan void().

Sampel ini menunjukkan kode yang menghasilkan kesalahan di bawah dan /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
}

Kode ini sebelumnya memberikan kesalahan ini:

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

Dengan /Zc:ternary, alasan kegagalan menjadi lebih jelas. Salah satu dari beberapa konvensi panggilan yang ditentukan implementasi dapat digunakan untuk menghasilkan setiap lambda. Namun, kompilator tidak memiliki aturan preferensi untuk membedakan kemungkinan tanda tangan lambda. Output baru terlihat seperti ini:

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

Sumber masalah umum yang ditemukan oleh /Zc:ternary berasal dari operator bersyarah yang digunakan dalam pemrograman meta templat. Beberapa tipe hasil berubah di bawah sakelar ini. Contoh berikut menunjukkan dua kasus di mana /Zc:ternary mengubah tipe hasil ekspresi kondisional dalam konteks non-pemrograman:

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

Perbaikan umumnya adalah menerapkan std::remove_reference sifat pada jenis hasil, jika diperlukan untuk mempertahankan perilaku lama.

Untuk informasi selengkapnya tentang masalah kesuaian di Visual C++, lihat Perilaku Nonstandar.

Untuk mengatur opsi pengkompilasi ini di lingkungan pengembangan Visual Studio

  1. Buka kotak dialog Halaman Properti proyek. Untuk detailnya, lihat Mengatur pengkompilasi C++ dan membuat properti di Visual Studio.

  2. Pilih halaman properti Properti>Konfigurasi C/C++>Baris Perintah.

  3. Ubah properti Opsi Tambahan untuk disertakan /Zc:ternary atau /Zc:ternary- lalu pilih OK.

Lihat juga

/Zc (Kesuaian)