Pertanyaan gcc dan clang secara implisit menginisialisasi argumen template selama resolusi operator yang berlebihan


Pertimbangkan kode ini:

struct A; // incomplete type

template<class T>
struct D { T d; };

template <class T>
struct B { int * p = nullptr; };

int main() {
    B<D<A>> u, v;
    u = v;  // doesn't compile; complain that D<A>::d has incomplete type
    u.operator=(v); // compiles
}

Demo. Sejak u.operator=(v) kompilasi tetapi u = v; tidak, di suatu tempat selama resolusi overload untuk ekspresi yang terakhir compiler harus memiliki implantasi secara implisit D<A> - Tapi saya tidak mengerti mengapa diperlukan instantiasi.

Untuk membuat hal-hal lebih menarik, kode ini dikompilasi:

struct A; // incomplete type

template<class T>
struct D; // undefined

template <class T>
struct B { int * p = nullptr; };

int main() {
    B<D<A>> u, v;
    u = v;
    u.operator=(v);
}

Demo.

Apa yang terjadi di sini? Kenapa u = v; menyebabkan implantasi implisit dari D<A> - Jenis yang tidak digunakan di tubuh BDefinisi - dalam kasus pertama tetapi bukan yang kedua?


32
2017-09-19 02:41


asal


Jawaban:


Inti dari masalah ini adalah ADL menendang kedalam:

N3797 - [basic.lookup.argdep]

Ketika ekspresi postfix dalam panggilan fungsi (5.2.2) adalah id yang tidak memenuhi syarat, ruang nama lain tidak dipertimbangkan   selama pencarian normal yang tidak memenuhi syarat (3.4.1) dapat dicari, dan di ruang nama tersebut, ruang lingkup namespace   fungsi teman atau deklarasi template fungsi (11.3) tidak terlihat lain dapat ditemukan.

berikut:

Untuk setiap tipe argumen T dalam pemanggilan fungsi, ada satu set nol atau lebih ruangnama terkait dan a   mengatur nol atau lebih kelas terkait untuk dipertimbangkan. [...] Set dari   ruang nama dan kelas ditentukan dengan cara berikut:

  • Jika T adalah tipe kelas [..] kelas terkaitnya adalah: ...   Ingat, jika T adalah spesialisasi template kelas, ruang nama dan kelas yang terkait juga mencakup: ruang nama dan kelas yang terkait dengan   jenis argumen template yang disediakan untuk parameter jenis template

D<A> adalah kelas yang terkait dan oleh karena itu dalam daftar menunggu giliran.

Sekarang untuk bagian yang menarik [temp.inst] / 1

Kecuali spesialisasi template kelas telah secara eksplisit dipakai (14.7.2) atau secara khusus terspesialisasi (14.7.3),   spesialisasi template kelas secara implisit dipakai [...] ketika kelengkapan jenis kelas mempengaruhi semantik program

Orang bisa berpikir bahwa kelengkapan tipe D<A> tidak mempengaruhi sama sekali semantik program itu, namun [basic.lookup.argdep] / 4 mengatakan:

Ketika mempertimbangkan namespace terkait, pencarian sama dengan pencarian yang dilakukan ketika namespace yang terkait digunakan sebagai kualifikasi (3.4.3.2)   kecuali itu:

[...]   Setiap fungsi teman lingkup ruang lingkup nama atau templat fungsi teman yang dideklarasikan di kelas terkait terlihat dalam masing-masing   ruang nama bahkan jika tidak terlihat selama pencarian biasa (11.3)

yaitu kelengkapan jenis kelas sebenarnya mempengaruhi deklarasi teman-teman -> kelengkapan tipe kelas karena itu mempengaruhi semantik program. Itu juga alasan mengapa sampel kedua Anda berfungsi.

TL; DR  D<A> adalah instantiated.

Poin menarik yang terakhir adalah mengapa ADL mulai di tempat pertama

u = v; // Triggers ADL
u.operator=(v); // Doesn't trigger ADL

§13.3.1.2 / 2 menyatakan bahwa tidak boleh ada non-anggota operator=(selain yang built-in). Bergabunglah dengan [over.match.oper] / 2:

Seperangkat kandidat non-anggota adalah hasil dari lookup operator @ tanpa pengecualian dalam konteksnya   ekspresi sesuai dengan aturan biasa untuk pencarian nama dalam panggilan fungsi yang tidak memenuhi syarat (3.4.2)   kecuali bahwa semua fungsi anggota diabaikan.

dan kesimpulan logisnya adalah: tidak ada gunanya melakukan pencarian ADL jika tidak ada bentuk non-anggota di tabel 11. Namun [temp.inst] p7 melemaskan ini:

Jika proses resolusi berlebihan dapat menentukan fungsi yang benar untuk memanggil tanpa instantiate definisi template kelas, tidak ditentukan apakah instantiasi itu benar-benar terjadi.

dan itulah alasan mengapa dentang memicu keseluruhan ADL -> implicit instantiation proses di tempat pertama.

Mulai dari r218330 (pada saat menulis ini, telah dilakukan beberapa menit yang lalu) perilaku ini diubah untuk tidak melakukan ADL operator= sama sekali.


Referensi

Terima kasih kepada Richard Smith dan David Blaikie yang telah membantu saya memahami hal ini.


19
2017-09-23 22:09



Yah, saya pikir dalam Visual Studio 2013 kode akan terlihat seperti (tanpa = nullptr):

  struct A; // incomplete type

  template<class T>
  struct D { T d; };

  template <class T>
  struct B { int * p; };

  int void_main() {
    B<D<A>> u, v;
    u = v;          //  compiles
    u.operator=(v); // compiles
    return 0;
    }

Dalam hal ini harus dikompilasi dengan baik hanya karena jenis yang tidak lengkap dapat digunakan untuk penggunaan spesialisasi kelas template tertentu.

Adapun kesalahan run-time - variabel v digunakan tanpa inisialisasi - itu benar - struct B tidak memiliki konstruktor => B :: p tidak diinisialisasi dan bisa berisi sampah.


-2
2017-09-23 16:14