Definisi C ++ 20 di luar kelas dalam kelas templat


12

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

Demo

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


2
Saya pribadi lebih suka dalam definisi kelas, menghindari fungsi template (dan pengurangan "masalah" (Tidak cocok untuk "c string" == Foo<std::string>("foo"))).
Jarod42

@ Jarod42 Saya sangat setuju, saya lebih suka definisi di kelas juga. Saya hanya terkejut mengetahui bahwa C ++ 20 memungkinkan kita untuk tidak mengulangi tanda tangan fungsi tiga kali ketika mendefinisikannya ouf-of-class, yang mungkin berguna dalam API publik di mana implementasinya dalam file .inl tersembunyi.
ProXicT

Saya belum melihat itu tidak mungkin. Bagaimana saya bisa menggunakannya sejauh ini tanpa masalah?
ALX23z

1
Hmmm, di temp.friend , tidak banyak berubah, terutama tidak 1.3 yang harus bertanggung jawab atas perilaku ini. Karena dentang tidak menerima kode Anda, saya condong ke arah gcc yang memiliki bug.
n314159

@ ALX23z Ia bekerja tanpa deklarasi out-of-class jika kelas tidak templated.
ProXicT

Jawaban:


2

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 pernyataan operator==.

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 sebelumnya operator==sebagai templat yang terlihat.

Inilah testcase yang lebih menarik:

template <typename T> struct Foo;

#ifdef WRONG_DECL
template <typename T> bool operator==(Foo<T> lhs, int); // #1
#endif

template <typename T> struct Foo {
  friend bool operator==<T>(Foo<T> lhs, float); // #2
};

template <typename T> bool operator==(Foo<T> lhs, float); // #3
Foo<int> f;

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 dari Foo<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 untuk operator==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 syarat operator==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.


Terima kasih atas penjelasan terperinci ini, sangat bagus!
ProXicT
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.