Hingga standar C ++ 20 dari C ++, ketika kami ingin mendefinisikan operator di luar kelas yang menggunakan beberapa anggota pribadi dari kelas templat, kami akan menggunakan konstruksi yang mirip dengan ini:
template <typename T>
class Foo;
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);
template <typename T>
class Foo {
public:
constexpr Foo(T k) : mK(k) {}
constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
T mK;
};
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
return lhs == rhs.mK;
}
int main() {
return 1 == Foo<int>(1) ? 0 : 1;
}
Karena C ++ 20, bagaimanapun, kita dapat menghilangkan deklarasi out-of-class, dengan demikian juga deklarasi forward, sehingga kita dapat pergi dengan:
template <typename T>
class Foo {
public:
constexpr Foo(T k) : mK(k) {}
constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
T mK;
};
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
return lhs == rhs.mK;
}
Sekarang, pertanyaan saya adalah, bagian mana dari C ++ 20 yang memungkinkan kita melakukannya? Dan mengapa ini tidak mungkin dalam standar C ++ sebelumnya?
Seperti yang ditunjukkan dalam komentar, dentang tidak menerima kode ini yang disajikan dalam demo, yang menunjukkan ini sebenarnya bug di gcc.
Saya mengajukan laporan bug pada bugzilla gcc
"c string" == Foo<std::string>("foo")
)).Jawaban:
GCC memiliki bug.
Pencarian nama selalu dilakukan untuk nama templat yang muncul sebelum a
<
, bahkan ketika nama yang dimaksud adalah nama yang dideklarasikan dalam deklarasi (teman, spesialisasi eksplisit, atau instantiasi eksplisit).Karena nama
operator==
dalam deklarasi teman adalah nama yang tidak memenuhi syarat dan tunduk pada pencarian nama dalam templat, aturan pencarian nama dua fase berlaku. Dalam konteks ini,operator==
bukan nama dependen (itu bukan bagian dari panggilan fungsi, jadi ADL tidak berlaku), jadi nama tersebut dilihat ke atas dan terikat pada titik di mana ia muncul (lihat [temp.nondep] paragraf 1). Contoh Anda salah bentuk karena pencarian nama ini tidak menemukan pernyataanoperator==
.Saya berharap GCC menerima ini dalam mode C ++ 20 karena P0846R0 , yang memungkinkan (misalnya)
operator==<T>(a, b)
untuk digunakan dalam templat bahkan jika tidak ada deklarasi sebelumnyaoperator==
sebagai templat yang terlihat.Inilah testcase yang lebih menarik:
Dengan
-DWRONG_DECL
, GCC dan Clang setuju bahwa program ini tidak berbentuk: pencarian tanpa syarat untuk deklarasi teman # 2, dalam konteks definisi templat, menemukan deklarasi # 1, yang tidak cocok dengan teman instantiated dariFoo<int>
. Deklarasi # 3 bahkan tidak dipertimbangkan, karena pencarian yang tidak memenuhi syarat dalam template tidak menemukannya.Dengan
-UWRONG_DECL
, GCC (dalam C ++ 17 dan sebelumnya) dan Clang setuju bahwa program ini salah bentuk karena alasan yang berbeda: pencarian yang tidak memenuhi syarat untukoperator==
jalur # 2 tidak menemukan apa pun.Tetapi dengan
-UWRONG_DECL
, GCC dalam mode C ++ 20 tampaknya memutuskan bahwa tidak apa-apa jika pencarian yang tidak memenuhi syaratoperator==
di # 2 gagal (mungkin karena P0846R0), dan kemudian muncul untuk mengulang pencarian dari konteks instantiation template, sekarang menemukan # 3, di pelanggaran aturan pencarian nama dua-fase yang normal untuk templat.sumber