Saya baru saja menemukan kesalahan berikut (dan menemukan solusinya online, tetapi tidak ada di Stack Overflow):
(.gnu.linkonce. [stuff]): referensi yang tidak ditentukan ke [metode] [file objek] :(. gnu.linkonce. [stuff]): referensi yang tidak terdefinisi ke `typeinfo for [classname] '
Mengapa orang bisa mendapatkan salah satu dari kesalahan linker "referensi tidak ditentukan untuk typeinfo" ini?
(Poin bonus jika Anda dapat menjelaskan apa yang terjadi di balik layar.)
virtual void abc() =0;
(jika versi dasar tidak pernah disebut)abc()
seperti itu Anda dapat dengan mudah lupa untuk mendefinisikan ulangabc()
di kelas turunan dan berpikir bahwa semuanya baik-baik saja, karena Anda masih dapat memanggil fungsi tanpa masalah. Praktik yang baik untuk mengimplementasikan fungsi virtual murni ditemukan dalam artikel ini , dan ini adalah untuk membuat fungsi cetak "Fungsi virtual murni disebut" dan kemudian crash program.= 0;
.Jawaban:
Salah satu alasan yang mungkin adalah karena Anda mendeklarasikan fungsi virtual tanpa mendefinisikannya.
Ketika Anda mendeklarasikannya tanpa mendefinisikannya di unit kompilasi yang sama, Anda menunjukkan bahwa itu didefinisikan di tempat lain - ini berarti fase linker akan mencoba menemukannya di salah satu unit kompilasi lainnya (atau perpustakaan).
Contoh mendefinisikan fungsi virtual adalah:
Dalam hal ini, Anda melampirkan definisi ke deklarasi, yang berarti penghubung tidak perlu menyelesaikannya nanti.
Garis
mendeklarasikan
fn()
tanpa mendefinisikannya dan akan menyebabkan pesan kesalahan yang Anda tanyakan.Ini sangat mirip dengan kode:
yang menyatakan bahwa integer
i
dideklarasikan di unit kompilasi lain yang harus diselesaikan pada waktu tautan (jikapi
tidak, tidak dapat diatur ke alamatnya).sumber
virtual void fn() = 0
adalah definisi. Itu bukan definisi, tetapi hanya deklarasi . Satu-satunya alasan penghubung tidak berusaha menyelesaikannya adalah bahwa entri VMT yang sesuai tidak akan merujuk ke fungsi tubuh (kemungkinan besar akan mengandung null-pointer). Namun, tidak ada yang melarang Anda memanggil fungsi virtual murni ini dengan cara non-virtual, yaitu dengan menggunakan nama yang sepenuhnya memenuhi syarat. Dalam hal ini linker akan mencari tubuh, dan Anda harus mendefinisikan fungsinya. Dan ya, Anda dapat mendefinisikan tubuh untuk fungsi virtual murni.Ini juga bisa terjadi ketika Anda mencampur
-fno-rtti
dan-frtti
kode. Maka Anda perlu memastikan bahwa setiap kelas, yangtype_info
diakses dalam-frtti
kode, dikompilasi dengan metode utama mereka-frtti
. Akses tersebut dapat terjadi ketika Anda membuat objek kelas, menggunakandynamic_cast
dll.[ sumber ]
sumber
Ini terjadi ketika fungsi virtual (non-murni) yang dinyatakan tidak ada. Dalam definisi kelas Anda, sesuatu seperti:
Harus ditentukan (sebaris atau dalam file sumber tertaut):
Atau dinyatakan sebagai virtual murni:
sumber
Mengutip dari manual gcc :
Dan sedikit lebih awal di halaman yang sama:
Jadi, kesalahan ini terjadi ketika "metode kunci" tidak ada definisi, seperti jawaban lain yang telah disebutkan.
sumber
Jika Anda menautkan satu .so ke yang lain, namun satu kemungkinan lagi adalah kompilasi dengan "-fvisibility = hidden" di gcc atau g ++. Jika kedua file .so dibuat dengan "-fvisibility = hidden" dan metode kuncinya tidak sama dengan .so seperti yang lain dari implementasi fungsi virtual, yang terakhir tidak akan melihat vtable atau ketik info dari yang pertama. Untuk tautan, ini terlihat seperti fungsi virtual yang tidak diimplementasikan (seperti pada jawaban paxdiablo dan cdleary).
Dalam hal ini, Anda harus membuat pengecualian untuk visibilitas kelas dasar dengan
dalam deklarasi kelas. Misalnya,
Solusi lain, tentu saja, adalah tidak menggunakan "-fvisibility = hidden." Itu tidak mempersulit hal-hal untuk kompiler dan linker, mungkin merugikan kinerja kode.
sumber
Jawaban sebelumnya benar, tetapi kesalahan ini juga bisa disebabkan oleh upaya untuk menggunakan typeid pada objek kelas yang tidak memiliki fungsi virtual. C ++ RTTI memerlukan vtable, sehingga kelas yang Anda inginkan untuk melakukan identifikasi tipe memerlukan setidaknya satu fungsi virtual.
Jika Anda ingin mengetik informasi untuk bekerja di kelas yang Anda tidak benar-benar menginginkan fungsi virtual, buat virtual destructor.
sumber
Saya hanya menghabiskan beberapa jam untuk kesalahan ini, dan sementara jawaban lain di sini membantu saya memahami apa yang sedang terjadi, mereka tidak memperbaiki masalah khusus saya.
Saya sedang mengerjakan proyek yang mengkompilasi menggunakan keduanya
clang++
dang++
. Saya tidak memiliki masalah menghubungkan menggunakanclang++
, tetapi mendapatkanundefined reference to 'typeinfo for
kesalahan dengang++
.Poin penting: Menghubungkan pesanan dengan
g++
. Jika Anda mendaftar pustaka yang ingin Anda tautkan dalam urutan yang salah, Anda bisa mendapatkantypeinfo
kesalahan.Lihat pertanyaan SO ini untuk detail lebih lanjut tentang menautkan pesanan dengan
gcc
/g++
.sumber
Kemungkinan solusi untuk kode yang berhubungan dengan pustaka RTTI dan non-RTTI:
a) Kompilasi ulang semuanya dengan -frtti atau -fno-rtti
b) Jika a) tidak memungkinkan untuk Anda, coba yang berikut ini:
Asumsikan libfoo dibangun tanpa RTTI. Kode Anda menggunakan libfoo dan kompilasi dengan RTTI. Jika Anda menggunakan kelas (Foo) di libfoo yang memiliki virtual, Anda cenderung mengalami kesalahan waktu tautan yang mengatakan: missing typeinfo for class Foo.
Tetapkan kelas lain (mis. FooAdapter) yang tidak memiliki virtual dan akan meneruskan panggilan ke Foo yang Anda gunakan.
Kompilasi FooAdapter di perpustakaan statis kecil yang tidak menggunakan RTTI dan hanya bergantung pada simbol libfoo. Berikan header untuk itu dan gunakan itu sebagai gantinya dalam kode Anda (yang menggunakan RTTI). Karena FooAdapter tidak memiliki fungsi virtual, itu tidak akan memiliki jenis info apa pun dan Anda akan dapat menautkan biner Anda. Jika Anda menggunakan banyak kelas berbeda dari libfoo, solusi ini mungkin tidak nyaman, tetapi ini permulaan.
sumber
Mirip dengan RTTI, diskusi NO-RTTI di atas, masalah ini juga dapat terjadi jika Anda menggunakan dynamic_cast dan gagal menyertakan kode objek yang berisi implementasi kelas.
Saya mengalami pembangunan masalah ini di Cygwin dan kemudian porting kode ke Linux. File make, struktur direktori, dan bahkan versi gcc (4.8.2) identik dalam kedua kasus, tetapi kode terhubung dan dioperasikan dengan benar di Cygwin tetapi gagal untuk terhubung di Linux. Red Hat Cygwin tampaknya telah membuat modifikasi kompiler / tautan yang menghindari persyaratan penautan kode objek.
Pesan kesalahan penghubung Linux mengarahkan saya ke jalur dynamic_cast dengan benar, tetapi pesan sebelumnya di forum ini membuat saya mencari implementasi fungsi yang hilang daripada masalah aktual: kode objek hilang. Solusi saya adalah mengganti fungsi tipe virtual di kelas dasar dan turunan, misalnya virtual int isSpecialType (), daripada menggunakan dynamic_cast. Teknik ini menghindari persyaratan untuk menautkan kode implementasi objek hanya untuk mendapatkan dynamic_cast agar berfungsi dengan baik.
sumber
Di kelas dasar (kelas dasar abstrak) Anda mendeklarasikan destruktor virtual dan karena Anda tidak dapat mendeklarasikan destruktor sebagai fungsi virtual murni, Anda harus mendefinisikannya di sini di kelas abstrak, hanya definisi dummy seperti virtual ~ base ( ) {} akan dilakukan, atau di salah satu kelas turunan.
Jika Anda gagal melakukan ini, Anda akan berakhir di "simbol yang tidak terdefinisi" pada waktu tautan. Karena VMT memiliki entri untuk semua fungsi virtual murni dengan NULL yang cocok karena memperbarui tabel tergantung pada implementasi di kelas turunan. Tetapi untuk fungsi non-murni tetapi virtual, perlu definisi pada waktu tautan sehingga dapat memperbarui tabel VMT.
Gunakan c ++ filt untuk menghilangkan simbol. Seperti $ c ++ filt _ZTIN10storageapi8BaseHostE akan menampilkan sesuatu seperti "typeinfo for storageapi :: BaseHost".
sumber
Saya punya banyak kesalahan ini sekarang. Apa yang terjadi adalah bahwa saya membagi kelas hanya file header menjadi file header dan file cpp. Namun, saya tidak memperbarui sistem build saya, jadi file cpp tidak bisa dikompilasi. Di antara hanya memiliki referensi yang tidak terdefinisi ke fungsi yang dideklarasikan di header tetapi tidak diimplementasikan, saya mendapat banyak kesalahan typeinfo ini.
Solusinya adalah menjalankan kembali sistem build untuk mengkompilasi dan menautkan file cpp baru.
sumber
dalam kasus saya, saya menggunakan perpustakaan pihak ketiga dengan file header dan file. saya mensubklasifikasikan satu kelas, dan tautan kesalahan seperti ini terjadi ketika saya mencoba untuk membuat instance subkelas saya.
seperti yang disebutkan oleh @sergiy, diketahui itu bisa menjadi masalah 'rtti', saya berhasil mengatasinya dengan meletakkan implementasi konstruktor ke dalam file .cpp yang terpisah dan menerapkan flag kompilasi '-fno-rtti' ke file tersebut . ini bekerja dengan baik.
karena saya masih tidak begitu jelas tentang internal dari kesalahan tautan ini, saya tidak yakin apakah solusi saya bersifat umum. Namun, saya pikir itu layak dicoba sebelum mencoba cara adaptor seperti yang disebutkan oleh @ Francois. dan tentu saja, jika semua kode sumber tersedia (tidak dalam kasus saya), lebih baik lakukan kompilasi ulang dengan '-frtti' jika memungkinkan.
satu hal lagi, jika Anda memilih untuk mencoba solusi saya, coba buat file terpisah sesederhana mungkin, dan jangan gunakan beberapa fitur mewah C ++. mengambil perhatian khusus pada meningkatkan hal-hal terkait, karena sebagian besar tergantung pada rtti.
sumber
Saya mendapatkan kesalahan yang sama ketika antarmuka saya (dengan semua fungsi virtual murni) memerlukan satu fungsi lagi dan saya lupa "null".
Saya punya
class ICommProvider { public: /** * @brief If connection is established, it sends the message into the server. * @param[in] msg - message to be send * @return 0 if success, error otherwise */ virtual int vaSend(const std::string &msg) = 0; /** * @brief If connection is established, it is waiting will server response back. * @param[out] msg is the message received from server * @return 0 if success, error otherwise */ virtual int vaReceive(std::string &msg) = 0; virtual int vaSendRaw(const char *buff, int bufflen) = 0; virtual int vaReceiveRaw(char *buff, int bufflen) = 0; /** * @bief Closes current connection (if needed) after serving * @return 0 if success, error otherwise */ virtual int vaClose(); };
Last vaClose bukan virtual sehingga dikompilasi tidak tahu di mana mendapatkan implementasi untuk itu dan dengan demikian menjadi bingung. pesan saya adalah:
Perubahan sederhana dari
untuk
memperbaiki masalah. semoga membantu
sumber
Saya menghadapi situasi yang jarang, tetapi ini dapat membantu teman-teman lain dalam situasi yang sama. Saya harus bekerja pada sistem yang lebih lama dengan gcc 4.4.7. Saya harus mengkompilasi kode dengan dukungan c ++ 11 atau lebih tinggi, jadi saya membangun versi terbaru gcc 5.3.0. Ketika membangun kode saya dan menautkan ke dependensi jika dependensi dibangun dengan kompiler yang lebih lama, maka saya mendapatkan 'referensi tidak terdefinisi ke' kesalahan meskipun saya dengan jelas mendefinisikan jalur penghubung dengan -L / path / ke / lib -llibname. Beberapa paket seperti boost dan proyek build with cmake biasanya memiliki kecenderungan untuk menggunakan kompiler yang lebih lama, dan mereka biasanya menyebabkan masalah seperti itu. Anda harus berusaha keras untuk memastikan mereka menggunakan kompiler yang lebih baru.
sumber
Dalam kasus saya ini murni masalah ketergantungan perpustakaan bahkan jika saya memiliki panggilan dynamic_cast. Setelah menambahkan ketergantungan yang cukup ke makefile masalah ini hilang.
sumber
Periksa apakah dependensi Anda dikompilasi tanpa
-f-nortti
.Untuk beberapa proyek Anda harus mengaturnya secara eksplisit, seperti di RocksDB:
sumber
Dalam kasus saya itu adalah fungsi virtual di kelas antarmuka yang tidak didefinisikan sebagai virtual murni.
Saya lupa
= 0
sedikit.sumber