'typeid' versus 'typeof' di C ++


159

Saya bertanya-tanya apa perbedaan antara typeiddan typeofdi C ++. Inilah yang saya tahu:

  • typeiddisebutkan dalam dokumentasi untuk type_info yang didefinisikan di C ++ file header TypeInfo .

  • typeofdidefinisikan dalam ekstensi GCC untuk C dan di pustaka C ++ Boost .

Juga, ini adalah tes kode tes yang saya buat di mana saya telah menemukan yang typeidtidak mengembalikan apa yang saya harapkan. Mengapa?

main.cpp

#include <iostream>  
#include <typeinfo>  //for 'typeid' to work  

class Person {  
    public:
    // ... Person members ...  
    virtual ~Person() {}  
};  

class Employee : public Person {  
    // ... Employee members ...  
};  

int main () {  
    Person person;  
    Employee employee;  
    Person *ptr = &employee;  
    int t = 3;  

    std::cout << typeid(t).name() << std::endl;  
    std::cout << typeid(person).name() << std::endl;   // Person (statically known at compile-time)  
    std::cout << typeid(employee).name() << std::endl; // Employee (statically known at compile-time)  
    std::cout << typeid(ptr).name() << std::endl;      // Person * (statically known at compile-time)  
    std::cout << typeid(*ptr).name() << std::endl;     // Employee (looked up dynamically at run-time  
                                                       // because it is the dereference of a pointer
                                                       // to a polymorphic class)  
 }  

keluaran:

bash-3.2$ g++ -Wall main.cpp -o main  
bash-3.2$ ./main   
i  
6Person  
8Employee  
P6Person  
8Employee

8
Menurut Anda bagaimana cara kode Anda tidak mencetak nama jenis yang tepat? Terlihat bagus untukku. String aktual yang dikembalikan oleh name()ditentukan oleh implementasi. Itu tidak harus nama pengenal C ++ yang valid, hanya sesuatu yang secara unik mengidentifikasi jenisnya. Sepertinya implementasi Anda menggunakan skema penyusunan-nama umum kompiler.
Rob Kennedy

Rob terima kasih! Saya mengharapkan nama-nama yang persis sama dengan yang saya lihat di en.wikipedia.org/wiki/Typeid. Apa yang bisa dilakukan oleh nama-mangling di sini?
Tim

Jika Anda baru untuk mengetik seperti saya: Anda memerlukan fungsi virtual dalam jenis dasar untuk mengaktifkan vtable atau baris terakhir akan mencetak jenis dasar.
jw_

Jawaban:


200

Bahasa C ++ tidak memiliki yang namanya typeof. Anda harus melihat beberapa ekstensi khusus-kompiler. Jika Anda berbicara tentang GCC typeof, maka fitur serupa ada di C ++ 11 melalui kata kunci decltype. Sekali lagi, C ++ tidak memiliki typeofkata kunci seperti itu .

typeidadalah operator bahasa C ++ yang mengembalikan informasi identifikasi jenis pada waktu berjalan. Ini pada dasarnya mengembalikan type_infoobjek, yang setara dengan type_infoobjek lain .

Perhatikan, bahwa satu-satunya properti yang didefinisikan dari type_infoobjek yang dikembalikan adalah yang setara dan setara, yaitu type_infoobjek yang menggambarkan tipe yang berbeda harus membandingkan yang tidak sama, sedangkan type_infoobjek yang menggambarkan tipe yang sama harus membandingkan yang sama. Segala sesuatu yang lain ditentukan oleh implementasi. Metode yang mengembalikan berbagai "nama" tidak dijamin untuk mengembalikan apa pun yang dapat dibaca oleh manusia, dan bahkan tidak dijamin untuk mengembalikan apa pun.

Perhatikan juga, bahwa hal di atas mungkin menyiratkan (meskipun standar tampaknya tidak menyebutkannya secara eksplisit) bahwa aplikasi berurutan typeiduntuk tipe yang sama dapat mengembalikan type_infoobjek yang berbeda (yang, tentu saja, masih harus membandingkan sama).


1
tidakkah ini membutuhkan pembaruan karena C ++ 11 memilikinya decltype? Saya tidak yakin apa itu kebijakan umum, tetapi karena pertanyaan itu ditandai, C++saya akan mengharapkannya merujuk pada standar terbaru. Retagging pertanyaan seperti C++03itu juga akan menjadi pilihan imho. Saya pribadi kadang-kadang agak bingung, karena saya harus menggunakan preC ++ 11 di tempat kerja dan kadang-kadang saya tidak yakin apa yang benar "pre11" atau "post11".
idclev 463035818

11
FYI, decltypebukan pengganti typeof. typeofbekerja pada tipe juga sementara decltypetidak. Sebagai contoh, typeof(int)adalah intketika decltype(int)kesalahan.
Shahbaz

1
+ msgstr " type_infoobjek yang menggambarkan tipe berbeda harus membandingkan yang tidak sama" . Sebenarnya, ini tidak dijamin . Operator ketidaksetaraan dihapus dalam C ++ 20 untuk (saya berasumsi) mencegah mengandalkan berbagai jenis membandingkan non-sama. Tetapi jika Anda memikirkannya, kesetaraan tidak aman jika ketidaksetaraan tidak aman.
Indiana Kernick

51

Perbedaan utama antara keduanya adalah sebagai berikut

  • typeof adalah konstruk waktu kompilasi dan mengembalikan tipe seperti yang didefinisikan pada waktu kompilasi
  • typeid adalah konstruk runtime dan karenanya memberikan informasi tentang tipe runtime nilai.

typeof Referensi: http://www.delorie.com/gnu/docs/gcc/gcc_36.html

Referensi typeid: https://en.wikipedia.org/wiki/Typeid


Terima kasih, JaredPar! Saya memiliki beberapa pertanyaan baru di pos yang diperbarui setelah membaca balasan Anda. Seperti jika memang benar bahwa pengembalian mereka digunakan untuk tujuan yang berbeda: pengembalian typeof digunakan sebagai jenis kata kunci yang dapat mendefinisikan variabel, tetapi pengembalian typeid tidak bisa?
Tim

26

typeiddapat beroperasi pada saat runtime, dan mengembalikan sebuah objek yang menggambarkan tipe run time dari objek, yang harus menjadi pointer ke objek kelas dengan metode virtual agar RTTI (informasi tipe run-time) disimpan di kelas. Itu juga dapat memberikan tipe waktu kompilasi dari ekspresi atau nama tipe, jika tidak diberi pointer ke kelas dengan informasi tipe run-time.

typeofadalah ekstensi GNU, dan memberi Anda jenis ekspresi apa pun pada waktu kompilasi. Ini bisa berguna, misalnya, dalam mendeklarasikan variabel sementara dalam makro yang dapat digunakan pada banyak jenis. Di C ++, Anda biasanya menggunakan templat .


5
Sejauh yang saya tahu, typeidakan menerima ekspresi apa pun, bukan hanya yang mengevaluasi objek dengan metode virtual. Selanjutnya, typeidakan menerima nama jenis , bukan hanya ekspresi. Anda dapat mengatakan typeid(5)atau typeid(std::string)jika Anda mau.
Rob Kennedy

1
Saya telah mengklarifikasi jawaban saya untuk menjelaskannya; typeid dapat mengembalikan informasi jenis run-time jika tersedia, tetapi akan memberikan informasi tipe waktu kompilasi untuk hal lain.
Brian Campbell

Terima kasih, Brian dan Rob! Saya memiliki beberapa pertanyaan baru di pos yang diperbarui setelah membaca balasan Anda.
Tim

22

Menjawab pertanyaan tambahan:

kode tes saya berikut untuk typeid tidak menampilkan nama tipe yang benar. apa yang salah?

Tidak ada yang salah. Apa yang Anda lihat adalah representasi string dari nama tipe. C ++ standar tidak memaksa kompiler untuk memancarkan nama kelas yang tepat, hanya terserah pelaksana (vendor kompiler) untuk memutuskan apa yang cocok. Singkatnya, nama-nama terserah kompiler.


Ini adalah dua alat yang berbeda. typeofmengembalikan tipe ekspresi, tetapi tidak standar. Di C ++ 0x ada sesuatu yang disebut decltypeAFAIK yang melakukan pekerjaan yang sama.

decltype(0xdeedbeef) number = 0; // number is of type int!
decltype(someArray[0]) element = someArray[0];

Padahal typeiddigunakan dengan tipe polimorfik. Misalnya, katakanlah yang catberasal animal:

animal* a = new cat; // animal has to have at least one virtual function
...
if( typeid(*a) == typeid(cat) )
{
    // the object is of type cat! but the pointer is base pointer.
}

Terima kasih, Arak! Saya baru saja memperbarui posting dengan beberapa pertanyaan baru. Silakan lihat jika memungkinkan.
Tim

4

typeid menyediakan tipe data pada saat runtime, ketika diminta. Typedef adalah konstruk waktu kompilasi yang mendefinisikan tipe baru seperti yang dinyatakan setelah itu. Tidak ada typeof di C ++ Output muncul sebagai (ditampilkan sebagai komentar tertulis):

std::cout << typeid(t).name() << std::endl;  // i
std::cout << typeid(person).name() << std::endl;   // 6Person
std::cout << typeid(employee).name() << std::endl; // 8Employee
std::cout << typeid(ptr).name() << std::endl;      // P6Person
std::cout << typeid(*ptr).name() << std::endl;     //8Employee

3

Anda dapat menggunakan Boost demangle untuk mendapatkan nama yang terlihat bagus:

#include <boost/units/detail/utility.hpp>

dan sesuatu seperti

To_main_msg_evt ev("Failed to initialize cards in " + boost::units::detail::demangle(typeid(*_IO_card.get()).name()) + ".\n", true, this);
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.