Bisakah templat fungsi anggota kelas menjadi virtual?


304

Saya telah mendengar bahwa templat fungsi anggota kelas C ++ tidak bisa virtual. Apakah ini benar?

Jika mereka bisa virtual, apa contoh skenario di mana seseorang akan menggunakan fungsi seperti itu?


12
Saya menghadapi masalah yang sama, dan juga belajar bahwa itu kontroversial menjadi virtual dan template pada saat yang sama. Solusi saya adalah menulis templat ajaib yang akan umum di antara kelas turunan dan memanggil fungsi virtual murni yang melakukan bagian khusus. Ini tentu saja terkait dengan sifat masalah saya, jadi mungkin tidak berfungsi dalam setiap kasus.
Tamás Szelei

Jawaban:


329

Template adalah semua tentang kode penghasil kompiler pada waktu kompilasi . Fungsi virtual adalah semua tentang sistem run-time yang mencari tahu fungsi mana yang harus dipanggil pada saat run-time .

Setelah sistem run-time menemukan itu perlu memanggil fungsi virtual templatized, kompilasi semua dilakukan dan kompiler tidak dapat menghasilkan contoh yang sesuai lagi. Karenanya Anda tidak dapat memiliki templat fungsi anggota virtual.

Namun, ada beberapa teknik yang kuat dan menarik yang berasal dari menggabungkan polimorfisme dan template, terutama yang disebut penghapusan tipe .


32
Saya tidak melihat alasan bahasa untuk ini, hanya alasan implementasi . vtables bukan bagian dari bahasa - hanya kompiler cara standar mengimplementasikan bahasa.
gerardw

16
Virtual functions are all about the run-time system figuring out which function to call at run-time- maaf tapi ini cara yang agak salah, dan cukup membingungkan. Itu hanya tipuan, dan tidak ada "runtime figuring out" yang terlibat, diketahui selama waktu kompilasi bahwa fungsi yang akan dipanggil adalah yang ditunjukkan oleh pointer n-th di vtable. "Mencari tahu" menyiratkan ada pemeriksaan jenis dan semacamnya, yang tidak terjadi. Once the run-time system figured out it would need to call a templatized virtual function- Apakah fungsinya virtual atau tidak diketahui pada waktu kompilasi.
dtech

9
@ddriver: 1. Jika kompiler melihat void f(concr_base& cb, virt_base& vb) { cb.f(); vb.f(); }, maka ia "tahu" fungsi apa yang dipanggil pada titik yang cb.f()dipanggil, dan tidak tahu itu untuk vb.f(). Yang terakhir harus ditemukan saat runtime , oleh sistem runtime . Apakah Anda ingin menyebutnya "mencari tahu", dan apakah ini lebih atau kurang efisien, tidak sedikit mengubah fakta ini.
sbi

9
@ddriver: 2. Contoh templat fungsi (anggota) adalah fungsi (anggota), jadi tidak ada masalah sama sekali dengan meletakkan pointer ke instance seperti itu ke dalam vtable. Tapi contoh templat mana yang diperlukan hanya diketahui saat pemanggil dikompilasi, sementara vtable diatur ketika kelas dasar dan kelas turunan dikompilasi. Dan ini semua dikompilasi secara terpisah. Lebih buruk lagi - kelas turunan baru dapat dihubungkan ke sistem yang sedang berjalan saat runtime (bayangkan browser Anda memuat plugin secara dinamis). Bahkan kode sumber pemanggil mungkin lama hilang ketika kelas turunan baru dibuat.
sbi

9
@sbi: Mengapa Anda membuat asumsi berdasarkan nama saya? Saya tidak bingung dengan generik dan template. Saya tahu bahwa generik Java adalah murni waktu berjalan. Anda tidak menjelaskan secara mendalam mengapa Anda tidak dapat memiliki templat fungsi anggota virtual di C ++, tetapi InQsitive melakukannya. Anda terlalu menyederhanakan templat dan mekanika virtual untuk 'mengkompilasi waktu' vs 'run time' dan menyimpulkan bahwa "Anda tidak dapat memiliki templat fungsi anggota virtual." Saya mereferensikan jawaban InQsitive, yang merujuk "C ++ Templates The Complete Guide". Saya tidak menganggap itu sebagai "melambaikan tangan". Semoga harimu menyenangkan.
Javanator

133

Dari C ++ Templates Panduan Lengkap:

Templat fungsi anggota tidak dapat dinyatakan virtual. Batasan ini dikenakan karena implementasi biasa dari mekanisme panggilan fungsi virtual menggunakan tabel ukuran tetap dengan satu entri per fungsi virtual. Namun, jumlah instantiations dari templat fungsi anggota tidak tetap sampai seluruh program telah diterjemahkan. Oleh karena itu, mendukung templat fungsi anggota virtual akan memerlukan dukungan untuk jenis mekanisme yang sama sekali baru dalam kompiler dan penghubung C ++. Sebaliknya, anggota biasa templat kelas bisa virtual karena jumlah mereka tetap ketika kelas dipakai


8
Saya pikir kompiler dan penghubung C ++ hari ini, terutama dengan dukungan optimasi waktu tautan, harus dapat menghasilkan vtables dan offset yang diperlukan pada waktu tautan. Jadi mungkin kita akan mendapatkan fitur ini di C ++ 2b?
Kai Petzke

33

C ++ tidak mengizinkan fungsi anggota template virtual sekarang. Alasan yang paling mungkin adalah kompleksitas penerapannya. Rajendra memberikan alasan yang bagus mengapa hal itu tidak dapat dilakukan saat ini tetapi mungkin dengan perubahan standar yang masuk akal. Terutama mengetahui berapa banyak contoh fungsi templated sebenarnya ada dan membangun vtable tampaknya sulit jika Anda mempertimbangkan tempat panggilan fungsi virtual. Orang standar hanya memiliki banyak hal lain yang harus dilakukan sekarang dan C ++ 1x adalah banyak pekerjaan untuk penulis kompiler juga.

Kapan Anda membutuhkan fungsi anggota templated? Saya pernah menemukan situasi seperti itu di mana saya mencoba untuk memperbaiki hierarki dengan kelas dasar virtual murni. Itu adalah gaya yang buruk untuk menerapkan strategi yang berbeda. Saya ingin mengubah argumen dari salah satu fungsi virtual ke tipe numerik dan alih-alih membebani fungsi anggota dan menimpa setiap kelebihan di semua sub-kelas saya mencoba menggunakan fungsi templat virtual (dan harus mencari tahu bahwa itu tidak ada) .)


5
@pmr: Fungsi virtual mungkin dipanggil dari kode yang bahkan tidak ada saat fungsi dikompilasi. Bagaimana kompiler akan menentukan instance mana dari fungsi anggota templat virtual (teoretis) untuk menghasilkan kode yang bahkan tidak ada?
sbi

2
@sbi: Ya, kompilasi terpisah akan menjadi masalah besar. Saya bukan ahli di C ++ kompiler sama sekali jadi saya tidak bisa menawarkan solusi. Seperti halnya fungsi templated secara umum, ia harus dipakai lagi di setiap unit kompilasi, bukan? Bukankah itu menyelesaikan masalah?
pm

2
@ sbi jika Anda mengacu pada pemuatan perpustakaan secara dinamis, itu masalah umum dengan kelas / fungsi templat, bukan hanya dengan metode templat virtual.
Oak

"C ++ tidak mengizinkan [...]" - akan sangat menghargai untuk melihat referensi ke standar (tidak masalah jika yang mutakhir ketika jawabannya ditulis atau yang diperbarui delapan tahun kemudian) ...
Aconcagua

19

Tabel Fungsi Virtual

Mari kita mulai dengan beberapa latar belakang pada tabel fungsi virtual dan cara kerjanya ( sumber ):

[20.3] Apa perbedaan antara fungsi anggota virtual dan non-virtual?

Fungsi anggota non-virtual diselesaikan secara statis. Yaitu, fungsi anggota dipilih secara statis (pada waktu kompilasi) berdasarkan jenis penunjuk (atau referensi) ke objek.

Sebaliknya, fungsi anggota virtual diselesaikan secara dinamis (saat run-time). Artinya, fungsi anggota dipilih secara dinamis (saat run-time) berdasarkan pada jenis objek, bukan jenis pointer / referensi ke objek itu. Ini disebut "pengikatan dinamis." Kebanyakan kompiler menggunakan beberapa varian dari teknik berikut: jika objek memiliki satu atau lebih fungsi virtual, kompiler menempatkan pointer tersembunyi di objek yang disebut "virtual-pointer" atau "v-pointer." V-pointer ini menunjuk ke tabel global yang disebut "virtual-table" atau "v-table."

Compiler membuat tabel-v untuk setiap kelas yang memiliki setidaknya satu fungsi virtual. Sebagai contoh, jika class Circle memiliki fungsi virtual untuk draw () dan move () dan resize (), akan ada tepat satu v-table yang terkait dengan class Circle, bahkan jika ada trilyun objek Circle, dan v-pointer dari masing-masing objek Circle akan menunjuk ke Circle v-table. V-table itu sendiri memiliki pointer ke masing-masing fungsi virtual di kelas. Sebagai contoh, Circle v-table akan memiliki tiga pointer: pointer ke Circle :: draw (), pointer ke Circle :: move (), dan pointer ke Circle :: resize ().

Selama pengiriman fungsi virtual, sistem run-time mengikuti v-pointer objek ke tabel-v kelas, kemudian mengikuti slot yang sesuai dalam tabel-v ke kode metode.

Overhead ruang-biaya dari teknik di atas adalah nominal: pointer tambahan per objek (tetapi hanya untuk objek yang perlu melakukan pengikatan dinamis), ditambah pointer tambahan per metode (tetapi hanya untuk metode virtual). Overhead biaya-waktu juga cukup nominal: dibandingkan dengan panggilan fungsi normal, panggilan fungsi virtual membutuhkan dua pengambilan tambahan (satu untuk mendapatkan nilai v-pointer, yang kedua untuk mendapatkan alamat metode). Tak satu pun dari aktivitas runtime ini terjadi dengan fungsi non-virtual, karena kompilator menyelesaikan fungsi non-virtual secara eksklusif pada waktu kompilasi berdasarkan pada jenis pointer.


Masalah saya, atau bagaimana saya datang ke sini

Saya mencoba menggunakan sesuatu seperti ini sekarang untuk kelas dasar cubefile dengan fungsi beban yang dioptimalkan templated yang akan diimplementasikan secara berbeda untuk berbagai jenis kubus (beberapa disimpan oleh pixel, beberapa oleh gambar, dll).

Beberapa kode:

virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

Apa yang saya inginkan, tetapi tidak dapat dikompilasi karena kombo templated virtual:

template<class T>
    virtual void  LoadCube(UtpBipCube<T> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

Saya akhirnya memindahkan deklarasi templat ke tingkat kelas . Solusi ini akan memaksa program untuk mengetahui tentang tipe data tertentu yang akan mereka baca sebelum membacanya, yang tidak dapat diterima.

Larutan

peringatan, ini tidak terlalu cantik tapi itu memungkinkan saya untuk menghapus kode eksekusi berulang

1) di kelas dasar

virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

2) dan di kelas anak-anak

void  LoadCube(UtpBipCube<float> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

template<class T>
void  LoadAnyCube(UtpBipCube<T> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1);

Perhatikan bahwa LoadAnyCube tidak dideklarasikan di kelas dasar.


Berikut jawaban stack overflow lain dengan penyelesaian : perlu solusi anggota template virtual .


1
Saya bertemu situasi yang sama, dan struktur warisan dari kelas massa. macro membantu.
ZFY

16

Kode berikut dapat dikompilasi dan dijalankan dengan benar, menggunakan MinGW G ++ 3.4.5 pada Window 7:

#include <iostream>
#include <string>

using namespace std;

template <typename T>
class A{
public:
    virtual void func1(const T& p)
    {
        cout<<"A:"<<p<<endl;
    }
};

template <typename T>
class B
: public A<T>
{
public:
    virtual void func1(const T& p)
    {
        cout<<"A<--B:"<<p<<endl;
    }
};

int main(int argc, char** argv)
{
    A<string> a;
    B<int> b;
    B<string> c;

    A<string>* p = &a;
    p->func1("A<string> a");
    p = dynamic_cast<A<string>*>(&c);
    p->func1("B<string> c");
    B<int>* q = &b;
    q->func1(3);
}

dan hasilnya adalah:

A:A<string> a
A<--B:B<string> c
A<--B:3

Dan kemudian saya menambahkan kelas X baru:

class X
{
public:
    template <typename T>
    virtual void func2(const T& p)
    {
        cout<<"C:"<<p<<endl;
    }
};

Ketika saya mencoba menggunakan kelas X di main () seperti ini:

X x;
x.func2<string>("X x");

g ++ melaporkan kesalahan berikut:

vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
al void X::func2(const T&)'

Jadi jelas bahwa:

  • fungsi anggota virtual dapat digunakan dalam templat kelas. Mudah bagi kompiler untuk membuat vtable
  • Tidak mungkin untuk mendefinisikan fungsi anggota templat kelas sebagai virtual, seperti yang Anda lihat, sulit untuk menentukan tanda tangan fungsi dan mengalokasikan entri vtable.

19
Templat kelas mungkin memiliki fungsi anggota virtual. Fungsi anggota mungkin bukan templat fungsi anggota dan fungsi anggota virtual.
James McNellis

1
sebenarnya gagal dengan gcc 4.4.3. Di sistem saya pasti Ubuntu 10,04
blueskin

3
Ini sama sekali berbeda dari apa yang ditanyakan. Di sini seluruh kelas dasar dibuat templated. Saya telah menyusun hal semacam ini sebelumnya. Ini akan dikompilasi di Visual Studio 2010 juga
ds-bos-msk

14

Tidak, mereka tidak bisa. Tapi:

template<typename T>
class Foo {
public:
  template<typename P>
  void f(const P& p) {
    ((T*)this)->f<P>(p);
  }
};

class Bar : public Foo<Bar> {
public:
  template<typename P>
  void f(const P& p) {
    std::cout << p << std::endl;
  }
};

int main() {
  Bar bar;

  Bar *pbar = &bar;
  pbar -> f(1);

  Foo<Bar> *pfoo = &bar;
  pfoo -> f(1);
};

memiliki banyak efek yang sama jika semua yang ingin Anda lakukan adalah memiliki antarmuka umum dan menunda implementasi ke subclass.


3
Ini dikenal sebagai CRTP jika ada yang penasaran.
Michael Choi

1
Tetapi ini tidak membantu untuk kasus-kasus, di mana seseorang memiliki hirarki kelas dan ingin dapat memanggil metode virtual pointer ke kelas dasar. FooPointer Anda memenuhi syarat karena Foo<Bar>, tidak dapat menunjuk ke Foo<Barf>atau Foo<XXX>.
Kai Petzke

@ KaiPetzke: Anda tidak dapat membuat pointer yang tidak dibatasi, tidak. Tetapi Anda dapat mem-template kode apa pun yang tidak perlu mengetahui tipe konkret, yang memiliki banyak efek yang sama (setidaknya secara konseptual - implementasi jelas sangat berbeda).
Tom

8

Tidak, fungsi anggota template tidak boleh virtual.


9
Keingintahuan saya adalah: Mengapa? Masalah apa yang dihadapi kompiler dalam melakukannya?
WannaBeGeek

1
Anda memerlukan deklarasi dalam cakupan (setidaknya, untuk mendapatkan tipe yang benar). Diperlukan oleh standar (dan bahasa) untuk memiliki deklarasi dalam cakupan untuk pengidentifikasi yang Anda gunakan.
dirkgently

4

Di jawaban lain fungsi templat yang diusulkan adalah fasad dan tidak menawarkan manfaat praktis.

  • Fungsi template berguna untuk menulis kode hanya sekali menggunakan jenis yang berbeda.
  • Fungsi virtual berguna untuk memiliki antarmuka umum untuk kelas yang berbeda.

Bahasa tidak memungkinkan fungsi template virtual tetapi dengan solusi dimungkinkan untuk memiliki keduanya, misalnya satu implementasi template untuk setiap kelas dan antarmuka umum virtual.

Namun demikian perlu untuk mendefinisikan untuk setiap kombinasi jenis template fungsi pembungkus virtual dummy:

#include <memory>
#include <iostream>
#include <iomanip>

//---------------------------------------------
// Abstract class with virtual functions
class Geometry {
public:
    virtual void getArea(float &area) = 0;
    virtual void getArea(long double &area) = 0;
};

//---------------------------------------------
// Square
class Square : public Geometry {
public:
    float size {1};

    // virtual wrapper functions call template function for square
    virtual void getArea(float &area) { getAreaT(area); }
    virtual void getArea(long double &area) { getAreaT(area); }

private:
    // Template function for squares
    template <typename T>
    void getAreaT(T &area) {
        area = static_cast<T>(size * size);
    }
};

//---------------------------------------------
// Circle
class Circle : public Geometry  {
public:
    float radius {1};

    // virtual wrapper functions call template function for circle
    virtual void getArea(float &area) { getAreaT(area); }
    virtual void getArea(long double &area) { getAreaT(area); }

private:
    // Template function for Circles
    template <typename T>
    void getAreaT(T &area) {
        area = static_cast<T>(radius * radius * 3.1415926535897932385L);
    }
};


//---------------------------------------------
// Main
int main()
{
    // get area of square using template based function T=float
    std::unique_ptr<Geometry> geometry = std::make_unique<Square>();
    float areaSquare;
    geometry->getArea(areaSquare);

    // get area of circle using template based function T=long double
    geometry = std::make_unique<Circle>();
    long double areaCircle;
    geometry->getArea(areaCircle);

    std::cout << std::setprecision(20) << "Square area is " << areaSquare << ", Circle area is " << areaCircle << std::endl;
    return 0;
}

Keluaran:

Luas kotak adalah 1, Luas lingkaran adalah 3.1415926535897932385

Coba di sini


3

Untuk menjawab bagian kedua dari pertanyaan:

Jika mereka bisa virtual, apa contoh skenario di mana seseorang akan menggunakan fungsi seperti itu?

Ini bukan hal yang tidak masuk akal untuk dilakukan. Misalnya, Java (di mana setiap metode virtual) tidak memiliki masalah dengan metode generik.

Salah satu contoh dalam C ++ menginginkan templat fungsi virtual adalah fungsi anggota yang menerima iterator generik. Atau fungsi anggota yang menerima objek fungsi generik.

Solusi untuk masalah ini adalah dengan menggunakan tipe erasure dengan boost :: any_range dan boost :: function, yang akan memungkinkan Anda untuk menerima iterator atau functor generik tanpa perlu menjadikan fungsi Anda sebagai templat.


6
Java generics adalah gula sintaksis untuk pengecoran. Mereka tidak sama dengan template.
Brice M. Dempsey

2
@ BriceM.Dempsey: Anda bisa mengatakan bahwa casting adalah cara Java mengimplementasikan obat generik, bukan sebaliknya ... dan secara semi-otomatis, hadiah use-case exclipy adalah IMO yang valid.
einpoklum

2

Ada solusi untuk 'metode templat virtual' jika rangkaian tipe untuk metode templat diketahui sebelumnya.

Untuk menunjukkan ide, dalam contoh di bawah ini hanya dua jenis yang digunakan ( intdan double).

Di sana, metode template 'virtual' ( Base::Method) memanggil metode virtual yang sesuai (salah satunya Base::VMethod) yang, pada gilirannya, memanggil implementasi metode template ( Impl::TMethod).

Satu hanya perlu menerapkan metode template TMethoddalam implementasi turunan ( AImpl, BImpl) dan digunakan Derived<*Impl>.

class Base
{
public:
    virtual ~Base()
    {
    }

    template <typename T>
    T Method(T t)
    {
        return VMethod(t);
    }

private:
    virtual int VMethod(int t) = 0;
    virtual double VMethod(double t) = 0;
};

template <class Impl>
class Derived : public Impl
{
public:
    template <class... TArgs>
    Derived(TArgs&&... args)
        : Impl(std::forward<TArgs>(args)...)
    {
    }

private:
    int VMethod(int t) final
    {
        return Impl::TMethod(t);
    }

    double VMethod(double t) final
    {
        return Impl::TMethod(t);
    }
};

class AImpl : public Base
{
protected:
    AImpl(int p)
        : i(p)
    {
    }

    template <typename T>
    T TMethod(T t)
    {
        return t - i;
    }

private:
    int i;
};

using A = Derived<AImpl>;

class BImpl : public Base
{
protected:
    BImpl(int p)
        : i(p)
    {
    }

    template <typename T>
    T TMethod(T t)
    {
        return t + i;
    }

private:
    int i;
};

using B = Derived<BImpl>;

int main(int argc, const char* argv[])
{
    A a(1);
    B b(1);
    Base* base = nullptr;

    base = &a;
    std::cout << base->Method(1) << std::endl;
    std::cout << base->Method(2.0) << std::endl;

    base = &b;
    std::cout << base->Method(1) << std::endl;
    std::cout << base->Method(2.0) << std::endl;
}

Keluaran:

0
1
2
3

NB: Base::Methodsebenarnya surplus untuk kode nyata ( VMethoddapat dibuat publik dan digunakan secara langsung). Saya menambahkannya sehingga terlihat seperti metode template 'virtual' yang sebenarnya.


Saya datang dengan solusi ini sambil menyelesaikan masalah di tempat kerja. Tampaknya mirip dengan Mark Essel di atas, tetapi, saya harap, lebih baik diimplementasikan dan dijelaskan.
sad1raf

Saya sudah memenuhi syarat ini sebagai kode kebingungan, dan Anda masih tidak menyiasati kenyataan bahwa Anda harus memodifikasi Basekelas asli setiap kali Anda perlu memanggil fungsi template dengan tipe argumen yang tidak kompatibel dengan yang diterapkan sejauh ini. Menghindari keharusan ini adalah maksud dari templat ...
Aconcagua

Pendekatan Essels sangat berbeda: Fungsi virtual biasa yang menerima contoh template yang berbeda - dan fungsi template terakhir di kelas turunan hanya berfungsi untuk menghindari duplikasi kode dan bahkan tidak memiliki bagian penghitung di kelas dasar ...
Aconcagua

2

Sementara pertanyaan yang lebih tua yang telah dijawab oleh banyak orang, saya percaya metode ringkas, tidak begitu berbeda dari yang lain diposting, adalah menggunakan makro kecil untuk membantu memudahkan duplikasi deklarasi kelas.

// abstract.h

// Simply define the types that each concrete class will use
#define IMPL_RENDER() \
    void render(int a, char *b) override { render_internal<char>(a, b); }   \
    void render(int a, short *b) override { render_internal<short>(a, b); } \
    // ...

class Renderable
{
public:
    // Then, once for each on the abstract
    virtual void render(int a, char *a) = 0;
    virtual void render(int a, short *b) = 0;
    // ...
};

Jadi sekarang, untuk mengimplementasikan subclass kami:

class Box : public Renderable
{
public:
    IMPL_RENDER() // Builds the functions we want

private:
    template<typename T>
    void render_internal(int a, T *b); // One spot for our logic
};

Manfaatnya di sini adalah, ketika menambahkan jenis yang baru didukung, semuanya dapat dilakukan dari header abstrak dan mengabaikan kemungkinan memperbaikinya dalam beberapa file sumber / header.


0

Setidaknya dengan gcc 5.4 fungsi virtual bisa menjadi anggota templat tetapi harus templat sendiri.

#include <iostream>
#include <string>
class first {
protected:
    virtual std::string  a1() { return "a1"; }
    virtual std::string  mixt() { return a1(); }
};

class last {
protected:
    virtual std::string a2() { return "a2"; }
};

template<class T>  class mix: first , T {
    public:
    virtual std::string mixt() override;
};

template<class T> std::string mix<T>::mixt() {
   return a1()+" before "+T::a2();
}

class mix2: public mix<last>  {
    virtual std::string a1() override { return "mix"; }
};

int main() {
    std::cout << mix2().mixt();
    return 0;
}

Keluaran

mix before a2
Process finished with exit code 0

0

Coba ini:

Tulis di classeder.h:

template <typename T>
class Example{
public:
    T c_value;

    Example(){}

    T Set(T variable)
    {
          return variable;
    }

    virtual Example VirtualFunc(Example paraM)
    {
         return paraM.Set(c_value);
    }

Periksa, jika bekerja dengan ini, untuk menulis kode ini di main.cpp:

#include <iostream>
#include <classeder.h>

int main()
{
     Example exmpl;
     exmpl.c_value = "Hello, world!";
     std::cout << exmpl.VirtualFunc(exmpl);
     return 0;
}
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.