g ++ referensi yang tidak ditentukan untuk typeinfo

209

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.)

cdleary
sumber
31
Saya tahu ini adalah posting lama, tetapi saya memiliki masalah yang sama hari ini, dan solusinya adalah dengan mendefinisikan fungsi virtual saya sebagai virtual abc () {} di kelas dasar, bukannya virtual abc (); yang memberi kesalahan.
Nav
15
lebih baik lagi sebagai virtual void abc() =0;(jika versi dasar tidak pernah disebut)
dhardy
3
@Nav: Jika Anda mendefinisikan abc()seperti itu Anda dapat dengan mudah lupa untuk mendefinisikan ulang abc()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.
HelloGoodbye
1
saya mengalami kesalahan yang sama. Saya telah menemukan bahwa mengubah urutan referensi ke "Lib" dapat membantu. saya baru saja memindahkan masalah lib dari awal hingga akhir daftar dan ini menyelesaikan masalah
javapowered
2
GAH. Ini sekarang setidaknya kedua kalinya saya menavigasi tepat ke halaman ini, untuk membaca komentar oleh @ hardy dan berkata pada diriku sendiri 'Doh'. Hanya menghabiskan 45 menit mencoba melacak perilaku gila dan yang saya butuhkan adalah = 0;.
dwanderson

Jawaban:

223

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:

virtual void fn() { /* insert code here */ }

Dalam hal ini, Anda melampirkan definisi ke deklarasi, yang berarti penghubung tidak perlu menyelesaikannya nanti.

Garis

virtual void fn();

mendeklarasikan fn()tanpa mendefinisikannya dan akan menyebabkan pesan kesalahan yang Anda tanyakan.

Ini sangat mirip dengan kode:

extern int i;
int *pi = &i;

yang menyatakan bahwa integer idideklarasikan di unit kompilasi lain yang harus diselesaikan pada waktu tautan (jika pitidak, tidak dapat diatur ke alamatnya).

paxdiablo
sumber
28
Tidak benar mengatakan itu virtual void fn() = 0adalah 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.
AnT
1
Dan terkadang seseorang bahkan harus mendeklarasikan tubuh untuk fungsi virtual murni.
tandai
3
Kompiler (g ++) akan memberi tahu Anda apa simbol yang hilang. Catatan: Dalam kasus penautan pustaka dinamis Anda mungkin mendapatkan nama yang rusak. Gunakan c ++ filt <mangledNameVariable> untuk mendapatkannya dalam bentuk yang dapat dibaca. Kesalahan typeinfo dengan nama kelas dalam kasus saya karena implementasi destruktor virtual yang hilang di beberapa kelas dasar.
chmike
1
Pertanyaan khusus menyebutkan bahwa itu adalah typeinfo yang hilang, yang ada hubungannya dengan rtti. Lihat komentar dari Damon di stackoverflow.com/questions/11904519/…
wilsonmichaelpatrick
1
@ gbmhunter, cukup adil. Buat perubahan.
paxdiablo
150

Ini juga bisa terjadi ketika Anda mencampur -fno-rttidan -frttikode. Maka Anda perlu memastikan bahwa setiap kelas, yang type_infodiakses dalam -frttikode, dikompilasi dengan metode utama mereka -frtti. Akses tersebut dapat terjadi ketika Anda membuat objek kelas, menggunakan dynamic_castdll.

[ sumber ]

Sergiy Belozorov
sumber
20
TERIMA KASIH BANYAK. Itu memperbaiki masalah saya setelah pencarian 5 jam.
steipete
1
tautan sumber sudah mati, itu pasti sama dengan permalink.gmane.org/gmane.comp.gcc.help/32475
math
1
Terima kasih telah menunjukkan ini. Halaman asli masih tersedia di sini: web.archive.org/web/20100503172629/http://www.pubbs.net/201004/…
Sergiy Belozorov
3
StackOverflow.com untuk menyelamatkan lagi! Saya berharap saya bisa lebih memilih sekali. Setelah membenturkan kepala saya pada keyboard selama satu jam, jawaban Anda adalah yang saya butuhkan.
spartygw
1
n +1 hidup diselamatkan dan masih terus bertambah :)
Gabriel
53

Ini terjadi ketika fungsi virtual (non-murni) yang dinyatakan tidak ada. Dalam definisi kelas Anda, sesuatu seperti:

virtual void foo();

Harus ditentukan (sebaris atau dalam file sumber tertaut):

virtual void foo() {}

Atau dinyatakan sebagai virtual murni:

virtual void foo() = 0;
cdleary
sumber
27

Mengutip dari manual gcc :

Untuk kelas polimorfik (kelas dengan fungsi virtual), objek type_info ditulis bersama dengan vtable [...] Untuk semua tipe lainnya, kita menuliskan objek type_info ketika digunakan: ketika menerapkan `typeid 'ke ekspresi, melempar objek, atau merujuk pada jenis dalam klausa tangkapan atau spesifikasi pengecualian.

Dan sedikit lebih awal di halaman yang sama:

Jika kelas mendeklarasikan fungsi virtual non-inline, non-murni, yang pertama dipilih sebagai "metode kunci" untuk kelas, dan vtable hanya dipancarkan di unit terjemahan di mana metode kunci didefinisikan.

Jadi, kesalahan ini terjadi ketika "metode kunci" tidak ada definisi, seperti jawaban lain yang telah disebutkan.

CesarB
sumber
2
Dalam kasus saya, saya memiliki kelas dasar yang menyatakan tetapi tidak mendefinisikan metode virtual yang bukan virtual murni. Setelah saya membuatnya murni virtual, yang saya maksudkan, kesalahan linker hilang.
Tatiana Racheva
@TatianaRacheva Terima kasih! Pelaporan kesalahan dari linker kurang membantu dan untuk antarmuka yang besar sangat mudah untuk kehilangan kekurangan '= 0;' untuk virtual murni!
rholmes
21

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

__attribute__ ((visibility("default")))

dalam deklarasi kelas. Misalnya,

class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}

Solusi lain, tentu saja, adalah tidak menggunakan "-fvisibility = hidden." Itu tidak mempersulit hal-hal untuk kompiler dan linker, mungkin merugikan kinerja kode.

manusia
sumber
1
Anda tidak perlu mengekspor (tidak menyembunyikan) kelas dasar jika abstrak atau tidak digunakan, hanya fungsi non-virtual, biasanya hanya konstruktor. The berasal kelas di sisi lain harus diekspor, jika mereka digunakan.
Chris Huang-Leaver
Rasanya seperti retas, tetapi hal itu menyelesaikan gejala di pihak saya. Terima kasih!
malat
16

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.

Tyler McHenry
sumber
2
Upmodded karena saya pikir ini lebih mungkin menjadi penyebab pesan kesalahan tertentu (sebagai lawan dari kasus yang lebih umum dari metode yang tidak terdefinisi ...)
Alastair
4
Satu hal yang saya harus terbiasa dengan SO adalah tidak mengacu pada jawaban "di atas" karena urutannya dapat berubah berdasarkan suara. Saya biasanya tidak merujuk ke jawaban lain sekarang karena mereka dapat dihapus juga. Keyakinan saya adalah bahwa jawaban harus berdiri sendiri. Namun saya masih merujuk pada nama pengguna untuk atribusi.
paxdiablo
Anda bisa menggunakan typeid tanpa vtable; lihat jawaban saya untuk kutipan dari manual gcc.
CesarB
11

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++dan g++. Saya tidak memiliki masalah menghubungkan menggunakan clang++, tetapi mendapatkan undefined reference to 'typeinfo forkesalahan dengan g++.

Poin penting: Menghubungkan pesanan dengan g++. Jika Anda mendaftar pustaka yang ingin Anda tautkan dalam urutan yang salah, Anda bisa mendapatkan typeinfokesalahan.

Lihat pertanyaan SO ini untuk detail lebih lanjut tentang menautkan pesanan dengan gcc/ g++.

dinkelk
sumber
Terima kasih!!! Saya telah menghabiskan lebih dari satu hari mencoba mencari tahu mengapa saya mendapatkan kesalahan ini dan tidak ada yang berhasil sampai saya melihat balasan ini dan yang Anda tautkan. Terima kasih banyak!!
Irene
10

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.

Francois
sumber
Ini untuk saya, menghubungkan ke perpustakaan dengan pengaturan RTTI yang berbeda.
marsh
6

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.

FNE
sumber
5

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".

Prashanth
sumber
3

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.

Claudiu
sumber
3

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.

uwydoc
sumber
2

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:

... TCPClient.o :(. Rodata + 0x38): referensi yang tidak ditentukan untuk `typeinfo for ICommProvider '

Perubahan sederhana dari

virtual int vaClose();

untuk

virtual int vaClose() = 0;

memperbaiki masalah. semoga membantu

Alex Paniutin
sumber
1

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.

Kemin Zhou
sumber
1

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.

Charlie
sumber
0

Periksa apakah dependensi Anda dikompilasi tanpa -f-nortti .

Untuk beberapa proyek Anda harus mengaturnya secara eksplisit, seperti di RocksDB:

USE_RTTI=1 make shared_lib -j4
Vitaly Isaev
sumber
0

Dalam kasus saya itu adalah fungsi virtual di kelas antarmuka yang tidak didefinisikan sebagai virtual murni.

class IInterface
{
public:
  virtual void Foo() = 0;
}

Saya lupa = 0sedikit.

Merinding
sumber