Mengapa fungsi inline C ++ di header?

120

NB Ini bukan pertanyaan tentang bagaimana menggunakan fungsi sebaris atau bagaimana mereka bekerja, lebih banyak mengapa mereka dilakukan sebagaimana adanya.

Deklarasi fungsi anggota kelas tidak perlu mendefinisikan fungsi inline, ini hanya implementasi sebenarnya dari fungsi tersebut. Misalnya, di file header:

struct foo{
    void bar(); // no need to define this as inline
}

Jadi mengapa implementasi inline dari fungsi kelas harus berada di file header? Mengapa saya tidak bisa meletakkan fungsi sebaris di .cppfile? Jika saya mencoba meletakkan definisi sebaris di .cppfile, saya akan mendapatkan kesalahan di sepanjang baris:

error LNK2019: unresolved external symbol 
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main 
1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe 
: fatal error LNK1120: 1 unresolved externals
thecoshman
sumber
@Charles Saya akan mengatakan bahwa tautan kedua saya serupa, tetapi saya bertanya lebih banyak tentang logika di balik mengapa inline bekerja seperti itu.
thecoshman
2
Dalam hal ini, saya pikir Anda mungkin telah salah paham baik "inline" atau "header files"; tak satu pun dari pernyataan Anda itu benar. Anda dapat memiliki implementasi inline dari fungsi anggota dan Anda dapat meletakkan definisi fungsi inline di file header, hanya saja itu mungkin bukan ide yang bagus. Bisakah Anda menjelaskan pertanyaan Anda?
CB Bailey
Posting edit, saya pikir Anda mungkin bertanya tentang situasi ketika inlinemuncul pada definisi tetapi bukan deklarasi sebelumnya vs sebaliknya . Jika demikian, ini dapat membantu: stackoverflow.com/questions/4924912/…
CB Bailey

Jawaban:

122

Definisi suatu inlinefungsi tidak harus dalam file header tetapi, karena aturan satu definisi ( ODR ) untuk fungsi sebaris, definisi yang identik untuk fungsi tersebut harus ada di setiap unit terjemahan yang menggunakannya.

Cara termudah untuk melakukannya adalah dengan meletakkan definisi di file header.

Jika Anda ingin meletakkan definisi suatu fungsi dalam satu file sumber maka Anda tidak boleh mendeklarasikannya inline. Sebuah fungsi tidak dideklarasikan inlinetidak berarti bahwa compiler tidak dapat menyebariskan fungsi tersebut.

Apakah Anda harus mendeklarasikan suatu fungsi inlineatau tidak biasanya merupakan pilihan yang harus Anda buat berdasarkan versi mana dari aturan definisi satu yang paling masuk akal untuk Anda ikuti; menambahkan inlinedan kemudian dibatasi oleh batasan berikutnya tidak masuk akal.

CB Bailey
sumber
Tetapi tidak kompilator mengkompilasi file .cpp, yang menyertakan file .h ... sehingga ketika mengkompilasi file .cpp, ia mengalami perlambatan dan juga file sumber. File header lain yang ditarik hanyalah milik mereka sehingga kompilator dapat 'mempercayai' bahwa fungsi-fungsi itu ada dan akan diterapkan di beberapa file sumber lain
thecoshman
1
Ini sebenarnya adalah jawaban yang jauh lebih baik dari saya, +1dari saya!
sbi
2
@thecoshman: Ada dua perbedaan. File sumber vs file header. Sesuai ketentuan, file header biasanya merujuk ke file sumber yang bukan dasar untuk unit terjemahan tetapi hanya #disertakan dari file sumber lain. Lalu ada deklarasi vs definisi. Anda dapat memiliki deklarasi atau definisi fungsi di file header atau file sumber 'normal'. Saya khawatir saya tidak yakin dengan apa yang Anda tanyakan dalam komentar Anda.
CB Bailey
jangan khawatir, saya mengerti mengapa sekarang ... meskipun saya tidak yakin siapa yang benar-benar menjawab yang ini. Kombinasi jawaban Anda dan @ Xanatos menjelaskannya untuk saya.
thecoshman
113

Ada dua cara untuk melihatnya:

  1. Fungsi sebaris didefinisikan di tajuk karena, untuk menyebariskan pemanggilan fungsi, penyusun harus dapat melihat isi fungsi. Agar kompiler naif dapat melakukan itu, badan fungsi harus berada dalam unit terjemahan yang sama dengan pemanggilan. (Kompiler modern dapat mengoptimalkan di seluruh unit terjemahan, sehingga pemanggilan fungsi mungkin sebaris meskipun definisi fungsi berada dalam unit terjemahan terpisah, tetapi pengoptimalan ini mahal, tidak selalu diaktifkan, dan tidak selalu didukung oleh penyusun)

  2. fungsi yang didefinisikan di header harus ditandai inlinekarena jika tidak, setiap unit terjemahan yang menyertakan header akan berisi definisi fungsi, dan linker akan mengeluh tentang beberapa definisi (pelanggaran terhadap One Definition Rule). Kata inlinekunci menekan ini, memungkinkan beberapa unit terjemahan berisi definisi (identik).

Kedua penjelasan tersebut benar-benar bermuara pada fakta bahwa inlinekata kunci tidak persis melakukan apa yang Anda harapkan.

Kompiler C ++ bebas untuk menerapkan pengoptimalan sebaris (mengganti panggilan fungsi dengan isi fungsi yang dipanggil, menghemat overhead panggilan) kapan saja, selama tidak mengubah perilaku program yang dapat diamati.

Kata inlinekunci mempermudah penyusun untuk menerapkan pengoptimalan ini, dengan memungkinkan definisi fungsi terlihat di beberapa unit terjemahan, tetapi menggunakan kata kunci tidak berarti penyusun harus menyebariskan fungsi, dan tidak menggunakan kata kunci tidak. melarang kompilator untuk menyebariskan fungsi.

jalf
sumber
23

Ini adalah batas kompiler C ++. Jika Anda meletakkan fungsi di tajuk, semua file cpp yang dapat dijadikan sebaris dapat melihat "sumber" fungsi Anda dan penyebarisan dapat dilakukan oleh kompilator. Selain itu, penyebarisan harus dilakukan oleh linker (setiap file cpp dikompilasi dalam file obj secara terpisah). Masalahnya adalah akan jauh lebih sulit untuk melakukannya di linker. Masalah serupa terjadi dengan kelas / fungsi "template". Mereka perlu dibuat instance-nya oleh compiler, karena linker akan mengalami masalah dalam membuat instance (membuat versi khusus). Beberapa kompilator / penaut yang lebih baru dapat melakukan kompilasi / penautan "dua lintasan" di mana kompilator melakukan lintasan pertama, kemudian penaut melakukan tugasnya dan memanggil kompilator untuk menyelesaikan hal-hal yang belum terselesaikan (inline / templates ...)

xanatos.dll
sumber
Oh begitu! ya, ini bukan untuk kelas itu sendiri yang menggunakan fungsi inline, kode lain yang menggunakan fungsi inline. Mereka hanya melihat file header untuk kelas yang sedang sebaris!
thecoshman
11
Saya tidak setuju dengan jawaban ini, ini bukan batas kompiler C ++; ini murni bagaimana aturan bahasa ditentukan. Aturan bahasa memungkinkan model kompilasi sederhana tetapi tidak melarang implementasi alternatif.
CB Bailey
3
Saya setuju dengan @Charles. Sebenarnya ada kompiler yang berfungsi sebaris di seluruh unit terjemahan, jadi ini jelas bukan karena batasan kompiler.
sbi
5
Meskipun jawaban ini tampaknya memiliki beberapa kesalahan teknis, itu membantu saya melihat bagaimana kompilator bekerja dengan file header dan semacamnya.
thecoshman
10

Alasannya adalah bahwa kompilator harus benar-benar melihat definisi tersebut agar dapat meletakkannya di tempat panggilan.

Ingatlah bahwa C dan C ++ menggunakan model kompilasi yang sangat sederhana, di mana kompiler selalu hanya melihat satu unit terjemahan dalam satu waktu. (Ini gagal untuk ekspor, yang merupakan alasan utama hanya satu vendor yang benar-benar menerapkannya.)

sbi
sumber
9

Kata inlinekunci c ++ menyesatkan, itu tidak berarti "sebariskan fungsi ini". Jika suatu fungsi didefinisikan sebagai sebaris, itu berarti bahwa itu dapat didefinisikan beberapa kali selama semua definisi sama. Ini sangat legal untuk fungsi yang ditandai inlinesebagai fungsi nyata yang dipanggil alih-alih memasukkan kode pada titik di mana ia dipanggil.

Mendefinisikan fungsi dalam file header diperlukan untuk template, karena misalnya kelas templated sebenarnya bukan kelas, ini adalah template untuk kelas yang dapat Anda buat beberapa variasi. Agar kompilator dapat, misalnya membuat Foo<int>::bar()fungsi saat Anda menggunakan template Foo untuk membuat kelas Foo , definisi sebenarnya dari Foo<T>::bar()harus terlihat.

Erik
sumber
Dan karena ini adalah template untuk kelas , itu tidak disebut kelas template , tapi template kelas .
sbi
4
Paragraf pertama sepenuhnya benar (dan saya harap saya dapat menekankan "menyesatkan"), tetapi saya tidak melihat perlunya non sequitur menjadi template.
Thomas Edleson
Beberapa kompiler akan menggunakannya sebagai petunjuk bahwa fungsinya mungkin bisa sebaris, tetapi memang, itu tidak dijamin akan menjadi sebaris hanya karena Anda mendeklarasikannya inline(juga tidak mendeklarasikannya inlinemenjamin bahwa itu tidak akan sebaris).
Keith M
4

Saya tahu ini adalah utas lama tetapi saya pikir saya harus menyebutkan externkata kunci itu. Saya baru-baru ini mengalami masalah ini dan menyelesaikannya sebagai berikut

Helper.h

namespace DX
{
    extern inline void ThrowIfFailed(HRESULT hr);
}

Helper.cpp

namespace DX
{
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            std::stringstream ss;
            ss << "#" << hr;
            throw std::exception(ss.str().c_str());
        }
    }
}
flamewave000
sumber
6
Hal ini biasanya tidak akan membuat fungsi benar-benar menjadi sebaris kecuali Anda menggunakan Pengoptimalan Seluruh Program (WPO).
Chuck Walbourn
3

Karena kompilator perlu melihatnya untuk menyebariskannya . Dan file header adalah "komponen" yang biasanya disertakan dalam unit terjemahan lain.

#include "file.h"
// Ok, now me (the compiler) can see the definition of that inline function. 
// So I'm able to replace calls for the actual implementation.
Leandro TC Melo
sumber
1

Fungsi Inline

Dalam C ++ makro tidak lain adalah fungsi sebaris. SO sekarang makro berada di bawah kendali compiler.

  • Penting : Jika kita mendefinisikan fungsi di dalam kelas, itu akan menjadi Inline secara otomatis

Kode fungsi Inline diganti di tempat pemanggilan, sehingga mengurangi overhead fungsi pemanggilan.

Dalam beberapa kasus, fungsi Inlining tidak dapat berfungsi, seperti

  • Jika variabel statis digunakan di dalam fungsi sebaris.

  • Jika fungsinya rumit.

  • Jika panggilan fungsi rekursif

  • Jika alamat fungsi diambil secara implisit atau eksplisit

Fungsi yang didefinisikan di luar kelas seperti di bawah ini mungkin menjadi sebaris

inline int AddTwoVar(int x,int y); //This may not become inline 

inline int AddTwoVar(int x,int y) { return x + y; } // This becomes inline

Fungsi yang didefinisikan di dalam kelas juga menjadi sebaris

// Inline SpeedMeter functions
class SpeedMeter
{
    int speed;
    public:
    int getSpeed() const { return speed; }
    void setSpeed(int varSpeed) { speed = varSpeed; }
};
int main()
{
    SpeedMeter objSM;
    objSM.setSpeed(80);
    int speedValue = A.getSpeed();
} 

Di sini, fungsi getSpeed ​​dan setSpeed ​​akan menjadi sebaris

Saurabh Raoot
sumber
eh, beberapa info bagus mungkin, tapi tidak benar-benar berusaha menjelaskan mengapa . Mungkin ya, tapi tidak menjelaskannya.
thecoshman
2
Pernyataan berikut tidak benar: "Penting: Jika kita mendefinisikan fungsi di dalam kelas, itu akan menjadi Inline secara otomatis" Bahkan jika Anda menulis "inline" di deklarasi / definisi, Anda dapat yakin bahwa itu sebenarnya sebaris. Bahkan tidak untuk template. Mungkin maksud Anda bahwa kompilator secara otomatis mengasumsikan kata kunci "sebaris", namun tidak harus mengikuti dan apa yang saya perhatikan adalah bahwa dalam banyak kasus ia tidak sebaris definisi dalam-header, bahkan untuk fungsi konsteks sederhana dengan aritmatika dasar.
Pablo Ariel
Hei terima kasih atas komentarnya ... Di bawah ini adalah baris dari Thinking in C ++ micc.unifi.it/bertini/download/programmazione/… Halaman 400 .. Silakan dicentang .. Silakan upvote jika Anda setuju. Terima kasih ..... Inlines di dalam kelas Untuk mendefinisikan fungsi inline, Anda biasanya harus mendahului definisi fungsi dengan kata kunci inline. Namun, ini tidak perlu di dalam definisi kelas. Setiap fungsi yang Anda tentukan di dalam definisi kelas secara otomatis menjadi sebaris.
Saurabh Raoot
Penulis buku itu dapat mengklaim apa yang mereka inginkan, karena mereka menulis buku dan bukan kode. Ini adalah sesuatu yang harus saya analisis secara mendalam untuk membuat demo 3d portabel saya sesuai dalam waktu kurang dari 64kb dengan menghindari kode sebaris sebanyak mungkin. Pemrograman adalah tentang fakta dan bukan agama, jadi tidak masalah jika beberapa "programmer tuhan" mengatakannya dalam sebuah buku jika itu tidak mewakili apa yang terjadi dalam praktik. Dan kebanyakan buku C ++ di luar sana adalah kumpulan nasihat buruk, di mana dari waktu ke waktu Anda bisa menemukan beberapa trik bagus untuk ditambahkan ke perbendaharaan Anda.
Pablo Ariel
Hai @PabloAriel Terima kasih ... Tolong analisis dan beri tahu saya .. Saya
bersedia