makna inline dalam antarmuka modul

24

Pertimbangkan file tajuk:

class T
{
private:
  int const ID;

public:
  explicit T(int const ID_) noexcept : ID(ID_) {}

  int GetID() const noexcept { return ID; }
};

atau, sebagai alternatif:

class T
{
private:
  int const ID;

public:
  explicit T(int const ID_) noexcept;

  int GetID() const noexcept;
};

inline T::T(int const ID_) noexcept : ID(ID_) {}

inline int T::GetID() const noexcept { return ID; }

Dalam dunia pra-modul, tajuk ini mungkin secara teks termasuk dalam banyak TU tanpa pelanggaran ODR. Selain itu, karena fungsi anggota yang terlibat relatif kecil, kompiler kemungkinan akan "inline" (menghindari panggilan fungsi saat menggunakan) fungsi-fungsi tersebut, atau bahkan mengoptimalkan beberapa instance dariT sekaligus.

Dalam laporan terbaru tentang pertemuan di mana C ++ 20 selesai, saya bisa membaca pernyataan berikut:

Kami mengklarifikasi makna inlinedalam antarmuka modul: tujuannya adalah bahwa badan fungsi yang tidak dinyatakan secara eksplisit inlinebukan bagian dari ABI modul, bahkan jika badan fungsi tersebut muncul di antarmuka modul. Untuk memberi penulis modul kontrol yang lebih besar atas ABI mereka, fungsi anggota yang didefinisikan dalam kelas di antarmuka modul tidak lagi secara implisit inline.

Saya tidak yakin saya tidak salah. Apakah itu berarti bahwa, dalam dunia modul, agar kompiler dapat mengoptimalkan panggilan fungsi jauh kita harus membuat anotasi mereka inlinebahkan jika mereka didefinisikan di kelas?

Jika demikian, apakah antarmuka modul berikut ini setara dengan header di atas?

export module M;

export
class T
{
private:
  int const ID;

public:
  inline explicit T(int const ID_) noexcept : ID(ID_) {}

  inline int GetID() const noexcept { return ID; }
};

Meskipun saya masih belum memiliki kompiler dengan dukungan modul, saya ingin mulai menggunakan inlineseperti itu ketika sesuai, untuk meminimalkan refactoring di masa depan.

kotak logam
sumber

Jawaban:

11

Apakah itu berarti bahwa, dalam dunia modul, agar kompiler dapat mengoptimalkan panggilan fungsi jauh kita harus membuat anotasi mereka inlinebahkan jika mereka didefinisikan di kelas?

Sampai taraf tertentu.

Inlining adalah optimasi "seolah-olah", dan inlining dapat terjadi bahkan antara unit terjemahan jika kompiler cukup pintar.

Yang mengatakan, inlining paling mudah ketika bekerja dalam satu unit terjemahan. Jadi, untuk mempromosikan inlining mudah, inlinefungsi -deklarasikan harus memiliki definisi yang disediakan di setiap unit terjemahan di mana ia digunakan. Ini tidak berarti kompiler pasti akan inline itu (atau tentu saja tidak inline inlinefungsi yang tidak memenuhi syarat), tetapi itu membuat banyak hal lebih mudah pada proses inlining, karena inlining terjadi dalam TU daripada di antara mereka.

Definisi anggota kelas yang didefinisikan dalam suatu kelas, dalam dunia pra-modul, dideklarasikan inline secara implisit. Mengapa? Karena definisi ada di dalam kelas. Dalam dunia pra-modul, definisi kelas yang dibagikan di antara TU dibagi dengan inklusi tekstual. Oleh karena itu, anggota yang ditentukan dalam suatu kelas akan didefinisikan dalam header yang dibagi di antara TU tersebut. Jadi jika beberapa TU menggunakan kelas yang sama, beberapa TU tersebut melakukannya dengan memasukkan definisi kelas dan definisi anggotanya yang dinyatakan dalam header.

Artinya, Anda tetap memasukkan definisi itu , jadi mengapa tidak membuatnya inline?

Tentu saja, ini berarti bahwa definisi suatu fungsi sekarang menjadi bagian dari teks kelas. Jika Anda mengubah definisi anggota yang dinyatakan dalam tajuk, ini memaksa kompilasi setiap file yang menyertakan tajuk itu, secara rekursif. Bahkan jika antarmuka kelas itu sendiri tidak berubah, Anda masih perlu melakukan kompilasi ulang. Jadi membuat fungsi seperti itu secara implisit inlinetidak mengubah ini, jadi sebaiknya Anda lakukan itu.

Untuk menghindari hal ini di dunia pra-modul, Anda cukup menentukan anggota dalam file C ++, yang tidak akan disertakan dalam file lain. Anda kehilangan inlining mudah, tetapi Anda mendapatkan waktu kompilasi.

Tapi ada satu hal: ini adalah artefak menggunakan inklusi tekstual sebagai sarana untuk memberikan kelas ke beberapa tempat.

Dalam dunia modular, Anda mungkin ingin mendefinisikan masing-masing fungsi anggota dalam kelas itu sendiri, seperti yang kita lihat dalam bahasa lain seperti Java, C #, Python, dan sejenisnya. Ini membuat kode lokalitas masuk akal, dan mencegah keharusan mengetik ulang tanda tangan fungsi yang sama, sehingga melayani kebutuhan KERING.

Tetapi jika semua anggota didefinisikan dalam definisi kelas, maka di bawah aturan lama, semua anggota itu akan menjadi inline. Dan agar modul memungkinkan fungsi berfungsi inline, artefak modul biner harus menyertakan definisi fungsi-fungsi tersebut. Yang berarti bahwa setiap kali Anda mengubah bahkan satu baris kode dalam definisi fungsi seperti itu, modul harus dibangun, bersama dengan setiap modul tergantung padanya, secara rekursif.

Menghapus inlinemodul implisit memberi pengguna kekuatan yang sama dengan yang mereka miliki di hari-hari inklusi tekstual, tanpa harus memindahkan definisi keluar dari kelas. Anda dapat memilih definisi fungsi mana yang merupakan bagian dari modul dan mana yang tidak.

Nicol Bolas
sumber
8

Ini berasal dari P1779 , baru diadopsi di Praha beberapa hari yang lalu. Dari proposal:

Makalah ini mengusulkan penghapusan status sebaris implisit dari fungsi yang didefinisikan dalam definisi kelas yang dilampirkan ke modul (bernama). Ini memungkinkan kelas untuk mendapat manfaat dari menghindari deklarasi berlebihan, mempertahankan fleksibilitas yang ditawarkan kepada penulis modul dalam mendeklarasikan fungsi dengan atau tanpa inline. Selain itu, ini memungkinkan teman yang disuntikkan dari templat kelas (yang tidak dapat didefinisikan secara umum di luar definisi kelas) menjadi tidak sama sekali. Ini juga menyelesaikan komentar NB US90 .

Makalah (antara lain) menghapus kalimat:

Fungsi yang didefinisikan dalam definisi kelas adalah fungsi inline.

dan menambahkan kalimat:

Dalam modul global, fungsi yang didefinisikan dalam definisi kelas secara implisit sebaris ([class.mfct], [class.friend]).


Contoh Anda dengan export module Makan menjadi setara modular dari program awal. Perhatikan bahwa kompiler sudah melakukan fungsi inline yang tidak dianotasi inline, hanya saja mereka juga menggunakan inlinekata kunci dalam heuristik mereka.

Barry
sumber
Jadi, fungsi dalam modul tanpa inlinekata kunci tidak akan pernah diuraikan oleh kompiler, benar?
metalfox
1
@metalfox Tidak, saya tidak percaya itu benar.
Barry
1
Saya melihat. Terima kasih. Ini seperti seperti yang didefinisikan dalam file cpp, yang tidak berarti itu tidak akan diuraikan pada waktu tautan.
metalfox