Selalu mempertimbangkan bahwa tajuk berikut, yang berisi kelas templat saya, termasuk dalam setidaknya dua .CPP
file, kode ini terkompilasi dengan benar:
template <class T>
class TClass
{
public:
void doSomething(std::vector<T> * v);
};
template <class T>
void TClass<T>::doSomething(std::vector<T> * v) {
// Do something with a vector of a generic T
}
template <>
inline void TClass<int>::doSomething(std::vector<int> * v) {
// Do something with a vector of int's
}
Namun perhatikan inline dalam metode spesialisasi. Hal ini diperlukan untuk menghindari kesalahan linker (dalam VS2008 adalah LNK2005) karena metode tersebut didefinisikan lebih dari sekali. Saya memahami ini karena AFAIK spesialisasi template lengkap sama dengan definisi metode sederhana.
Jadi, bagaimana cara menghapusnya inline
? Kode tidak boleh diduplikasi dalam setiap penggunaannya. Saya telah mencari di Google, membaca beberapa pertanyaan di SO dan mencoba banyak solusi yang disarankan tetapi tidak ada yang berhasil dibuat (setidaknya tidak di VS 2008).
Terima kasih!
Jawaban:
Seperti fungsi sederhana, Anda dapat menggunakan deklarasi dan implementasi. Masukkan deklarasi header Anda:
template <> void TClass<int>::doSomething(std::vector<int> * v);
dan terapkan implementasi ke salah satu file cpp Anda:
template <> void TClass<int>::doSomething(std::vector<int> * v) { // Do somtehing with a vector of int's }
Jangan lupa untuk menghapus inline (saya lupa dan mengira solusi ini tidak akan berhasil :)). Diperiksa di VC ++ 2005
sumber
inline
saat menyalin / menempel. Cara ini berhasil!Anda perlu memindahkan definisi spesialisasi ke file CPP. Spesialisasi fungsi anggota kelas template diperbolehkan meskipun fungsi tidak dideklarasikan sebagai template.
sumber
Tidak ada alasan untuk menghapus kata kunci sebaris.
Itu tidak mengubah arti kode dengan cara apapun.
sumber
inline
kata kunci menghasilkan fungsi yang sebenarnya sebaris (standar mengatakan bahwa kompilator harus menganggapnya sebagai petunjuk), maka salinan tambahan tersebut tidak dapat dihapus. Namun demikian, ini hanya petunjuk untuk sebaris (efek utamanya adalah mengatakan "jangan menghasilkan kesalahan pada tabrakan tautan dengan cara tertentu")Jika Anda ingin menghapus sebaris karena alasan apa pun, solusi maksim1000 benar-benar valid.
Namun, dalam komentar Anda, tampaknya Anda percaya bahwa kata kunci inline berarti bahwa fungsi dengan semua isinya selalu sebaris tetapi AFAIK yang sebenarnya sangat bergantung pada pengoptimalan kompiler Anda.
Mengutip dari C ++ FAQ
Jadi, kecuali Anda tahu bahwa fungsi itu benar-benar akan membengkak yang dapat dieksekusi atau kecuali Anda ingin menghapusnya dari tajuk definisi templat karena alasan lain, Anda benar-benar dapat membiarkannya di tempatnya tanpa membahayakan.
sumber
Saya ingin menambahkan bahwa masih ada alasan bagus untuk menyimpan
inline
kata kunci di sana jika Anda juga ingin meninggalkan spesialisasi di file header.Referensi: https://stackoverflow.com/a/4445772/1294184
sumber
Ini sedikit OT, tapi saya pikir saya akan meninggalkan ini di sini kalau-kalau ini membantu orang lain. Saya sedang mencari di Google tentang spesialisasi template yang membawa saya ke sini, dan sementara jawaban @ maxim1000 benar dan pada akhirnya membantu saya menemukan masalah saya, saya tidak berpikir itu sangat jelas.
Situasi saya sedikit berbeda (tetapi saya pikir cukup mirip untuk meninggalkan jawaban ini) daripada OP. Pada dasarnya, saya menggunakan pustaka pihak ketiga dengan semua jenis kelas yang berbeda yang mendefinisikan "tipe status". Inti dari jenis ini hanyalah
enum
s, tetapi semua kelas mewarisi dari induk umum (abstrak) dan menyediakan fungsi utilitas yang berbeda, seperti kelebihan beban operator danstatic toString(enum type)
fungsi. Setiap statusenum
berbeda satu sama lain dan tidak terkait. Misalnya, yang satuenum
memiliki kolomNORMAL, DEGRADED, INOPERABLE
, yang lain memilikiAVAILBLE, PENDING, MISSING
, dll. Perangkat lunak saya bertugas mengelola berbagai jenis status untuk berbagai komponen. Ternyata saya ingin memanfaatkantoString
fungsi untuk inienum
kelas, tetapi karena mereka abstrak saya tidak bisa membuat instance secara langsung. Saya dapat memperluas setiap kelas yang ingin saya gunakan, tetapi akhirnya saya memutuskan untuk membuattemplate
kelas, di manatypename
status konkret apa pun yangenum
saya pedulikan. Mungkin beberapa perdebatan dapat terjadi tentang keputusan itu, tetapi saya merasa itu adalah pekerjaan yang jauh lebih sedikit daripada memperluas setiapenum
kelas abstrak dengan kelas kustom saya sendiri dan menerapkan fungsi abstrak. Dan tentu saja dalam kode saya, saya hanya ingin dapat memanggil.toString(enum type)
dan mencetak representasi string ituenum
. Karena semuaenum
itu sama sekali tidak terkait, mereka masing-masing memiliki miliknya sendiritoString
fungsi yang (setelah beberapa penelitian yang saya pelajari) harus dipanggil menggunakan spesialisasi template. Itu membawaku ke sini. Di bawah ini adalah MCVE dari apa yang harus saya lakukan untuk membuat ini bekerja dengan benar. Dan sebenarnya solusi saya sedikit berbeda dari @ maxim1000.Ini adalah file header (sangat disederhanakan) untuk
enum
s. Pada kenyataannya, setiapenum
kelas didefinisikan dalam filenya sendiri. File ini mewakili file header yang diberikan kepada saya sebagai bagian dari pustaka yang saya gunakan:// file enums.h #include <string> class Enum1 { public: enum EnumerationItem { BEARS1, BEARS2, BEARS3 }; static std::string toString(EnumerationItem e) { // code for converting e to its string representation, // omitted for brevity } }; class Enum2 { public: enum EnumerationItem { TIGERS1, TIGERS2, TIGERS3 }; static std::string toString(EnumerationItem e) { // code for converting e to its string representation, // omitted for brevity } };
menambahkan baris ini hanya untuk memisahkan file berikutnya ke dalam blok kode yang berbeda:
// file TemplateExample.h #include <string> template <typename T> class TemplateExample { public: TemplateExample(T t); virtual ~TemplateExample(); // this is the function I was most concerned about. Unlike @maxim1000's // answer where (s)he declared it outside the class with full template // parameters, I was able to keep mine declared in the class just like // this std::string toString(); private: T type_; }; template <typename T> TemplateExample<T>::TemplateExample(T t) : type_(t) { } template <typename T> TemplateExample<T>::~TemplateExample() { }
file berikutnya
// file TemplateExample.cpp #include <string> #include "enums.h" #include "TemplateExample.h" // for each enum type, I specify a different toString method, and the // correct one gets called when I call it on that type. template <> std::string TemplateExample<Enum1::EnumerationItem>::toString() { return Enum1::toString(type_); } template <> std::string TemplateExample<Enum2::EnumerationItem>::toString() { return Enum2::toString(type_); }
file berikutnya
// and finally, main.cpp #include <iostream> #include "TemplateExample.h" #include "enums.h" int main() { TemplateExample<Enum1::EnumerationItem> t1(Enum1::EnumerationItem::BEARS1); TemplateExample<Enum2::EnumerationItem> t2(Enum2::EnumerationItem::TIGERS3); std::cout << t1.toString() << std::endl; std::cout << t2.toString() << std::endl; return 0; }
dan keluaran ini:
Tidak tahu apakah ini adalah solusi ideal untuk menyelesaikan masalah saya, tetapi berhasil untuk saya. Sekarang, tidak peduli berapa banyak jenis enumerasi yang akhirnya saya gunakan, yang harus saya lakukan hanyalah menambahkan beberapa baris untuk
toString
metode di file .cpp, dan saya dapat menggunakan metode perpustakaan yang sudah ditentukantoString
tanpa menerapkannya sendiri dan tanpa memperluas masing-masingenum
kelas yang ingin saya gunakan.sumber