Bir sınıf üyesi işlev şablonu sanal olabilir mi?


304

C ++ sınıf üyesi işlev şablonlarının sanal olamayacağını duydum. Bu doğru mu?

Sanal olabilirlerse, böyle bir işlevi kullanacağı bir senaryo örneği nedir?


12
Benzer bir sorunla karşılaştım ve aynı zamanda sanal ve şablon olmanın tartışmalı olduğunu öğrendim. Benim çözümüm, türetilmiş sınıflar arasında ortak olacak şablon sihirini yazmak ve özel kısmı yapan saf bir sanal işlev çağırmaktı. Bu elbette sorunumun doğası ile ilgilidir, bu yüzden her durumda işe yaramayabilir.
Tamás Szelei

Yanıtlar:


329

Şablonlar derleyicinin derleme zamanında kod üretmesiyle ilgilidir . Sanal fonksiyonlar çalışma zamanı sistemi de aramaya hangi işlevi bulmakla ilgili olan çalışma zamanı .

Çalışma zamanı sistemi geçici bir sanal işlev çağırması gerektiğini anladıktan sonra, derleme tamamlanır ve derleyici artık uygun örneği oluşturamaz. Bu nedenle, sanal üye işlev şablonlarınız olamaz.

Bununla birlikte, polimorfizm ve şablonların, özellikle de tip silme olarak birleştirilmesinden kaynaklanan birkaç güçlü ve ilginç teknik vardır .


32
Bunun için bir dil nedeni görmüyorum , sadece uygulama nedenlerini görüyorum . vtables dilin bir parçası değil - sadece derleyicilerin dili uygulama şekli.
gerardw

16
Virtual functions are all about the run-time system figuring out which function to call at run-time- üzgünüm ama bu oldukça yanlış bir yol ve oldukça kafa karıştırıcı. Sadece dolaylıdır ve bir "çalışma zamanı çözme" işlemi söz konusu değildir, derleme zamanında çağrılacak işlevin, vtable'daki n. İşaretçi tarafından işaret edilen işlev olduğu bilinmektedir. "Anlaşılması", tip kontrolleri olduğu anlamına gelir ve durum böyle değildir. Once the run-time system figured out it would need to call a templatized virtual function- derleme zamanında işlevin sanal olup olmadığı bilinir.
dtech

9
@ddriver: 1. Eğer derleyiciler görürse void f(concr_base& cb, virt_base& vb) { cb.f(); vb.f(); }, hangi fonksiyonun çağrıldığı noktada çağrıldığını "bilir" cb.f()ve bunu bilmez vb.f(). İkincisi çalışma zamanında , çalışma zamanı sistemi tarafından bulunmalıdır . Buna "çözme" demek isteyip istemediğinizi ve bunun az ya da çok etkili olup olmadığını, bu gerçekleri biraz değiştirmez.
sbi

9
@ddriver: 2. (üye) işlev şablonlarının örnekleri (üye) işlevleridir, bu nedenle vtable'a böyle bir örneğe bir işaretçi koymakla ilgili hiçbir sorun yoktur. Ancak hangi şablon örneklerine ihtiyaç duyulduğu yalnızca arayan derlendiğinde bilinir, oysa taban sınıf ve türetilmiş sınıflar derlendiğinde tablolar ayarlanır. Ve bunların hepsi ayrı ayrı derleniyor. Daha da kötüsü, yeni türetilmiş sınıflar çalışma zamanında çalışan sistemlere bağlanabilir (tarayıcınızın bir eklentiyi dinamik olarak yüklediğini düşünün). Yeni bir türetilmiş sınıf oluşturulduğunda arayanın kaynak kodu bile uzun süre kaybolabilir.
sbi

9
@sbi: İsmime dayanarak neden varsayımlar yapıyorsun? Jenerikler ve şablonları karıştırmadım. Java'nın jeneriklerinin tamamen çalışma zamanı olduğunu biliyorum. C ++ 'da sanal üye işlev şablonlarına neden sahip olamayacağınızı tam olarak açıklamadınız, ancak InQsitive bunu yaptı. Şablonu ve sanal mekaniği 'zamanı derlemek' ile 'çalışma süresi' arasında aşırı basitleştirdiniz ve "sanal üye işlev şablonlarına sahip olamayacağınız" sonucuna vardınız. InQsitive'ın "C ++ Templates The Complete Guide" cevabına atıfta bulundum. Bunun "el sallama" olduğunu düşünmüyorum. İyi günler.
Javanator

133

C ++ Şablonlarından Komple Kılavuz:

Üye işlev şablonları sanal olarak bildirilemez. Bu kısıtlama, sanal işlev çağrısı mekanizmasının olağan uygulaması, sanal işlev başına bir giriş içeren sabit boyutlu bir tablo kullandığından uygulanır. Ancak, üye işlev şablonunun örneklerinin sayısı, tüm program çevrilene kadar sabitlenmez. Bu nedenle, sanal üye işlev şablonlarını desteklemek için C ++ derleyicilerinde ve bağlayıcılarında yepyeni bir mekanizma için destek gerekir. Buna karşılık, sınıf şablonlarının sıradan üyeleri sanal olabilir, çünkü sınıf somutlaştırıldığında sayıları sabittir


8
Bence bugünün C ++ derleyicisi ve bağlayıcıları, özellikle bağlantı süresi optimizasyonu desteği ile, bağlantı zamanında gerekli vtables ve ofsetleri oluşturmak mümkün olmalıdır. Belki bu özelliği C ++ 2b'de alacağız?
Kai Petzke

33

C ++ şu anda sanal şablon üye işlevlerine izin vermiyor. En olası neden, onu uygulamanın karmaşıklığıdır. Rajendra şu anda neden yapılamamasının iyi bir nedenini veriyor, ancak standardın makul değişiklikleri ile mümkün olabilir. Özellikle sanal işlev çağrısının yerini düşünürseniz, şablonlanmış bir işlevin kaç tane örneğinin gerçekte var olduğunu ve vtable'ı oluşturmak zor görünüyor. Standartlar insanların şu anda yapacak başka şeyleri var ve C ++ 1x derleyici yazarları için de çok iş.

Geçici bir üye işlevine ne zaman ihtiyaç duyarsınız? Bir zamanlar saf bir sanal temel sınıfla bir hiyerarşiyi yeniden düzenlemeye çalıştığım bir durumla karşılaştım. Farklı stratejileri uygulamak için zayıf bir tarzdı. Sanal işlevlerden birinin bağımsız değişkenini sayısal bir türe değiştirmek ve üye işlevini aşırı yüklemek yerine, sanal şablon işlevlerini kullanmaya çalıştığım tüm alt sınıflardaki her aşırı yükü geçersiz kılmak yerine (ve bunların var olmadığını öğrenmek zorunda kaldım) .)


5
@ pmr: Sanal bir işlev, işlev derlendiğinde bile var olmayan koddan çağrılabilir. Derleyici, var olmayan bir kod için hangi (teorik) sanal şablon üye işlevinin hangi örneklerini üreteceğini nasıl belirler?
sbi

2
@sbi: Evet, ayrı derleme çok büyük bir sorun olurdu. C ++ derleyicilerinde uzman değilim, bu yüzden bir çözüm sunamıyorum. Genelde temperlenmiş fonksiyonlarda olduğu gibi, her derleme biriminde tekrar başlatılmalıdır, değil mi? Bu sorunu çözmez mi?
pmr

2
@ dinamik olarak kitaplıkları yüklemeyi kastediyorsanız, bu yalnızca sanal şablon yöntemleriyle değil, şablon sınıflarıyla / işlevleriyle ilgili genel bir sorundur.
Meşe

"C ++ izin vermez [...]" - standarda referans görmeyi takdir eder (cevap yazılırken güncel olan veya sekiz yıl sonra güncel olan olursa olsun) ...
Aconcagua

19

Sanal İşlev Tabloları

Sanal işlev tabloları ve bunların nasıl çalıştığı hakkında bazı bilgilerle başlayalım ( kaynak ):

[20.3] Sanal ve sanal olmayan üye işlevlerinin çağrılması arasındaki fark nedir?

Sanal olmayan üye işlevleri statik olarak çözümlenir. Yani, üye işlevi, nesneye yönelik işaretçinin (veya başvurunun) türüne göre statik olarak (derleme zamanında) seçilir.

Buna karşılık, sanal üye işlevleri dinamik olarak (çalışma zamanında) çözülür. Yani üye işlevi, o nesneye işaretçi / referansın türüne değil, nesnenin türüne göre dinamik olarak (çalışma zamanında) seçilir. Buna "dinamik ciltleme" denir. Çoğu derleyici aşağıdaki tekniğin bazı varyantlarını kullanır: nesnenin bir veya daha fazla sanal işlevi varsa, derleyici nesneye "sanal-işaretçi" veya "v-işaretçisi" adı verilen gizli bir işaretçi koyar. Bu v-işaretçisi "sanal tablo" veya "v-tablosu" adlı genel bir tabloyu gösterir.

Derleyici, en az bir sanal işlevi olan her sınıf için bir v-tablosu oluşturur. Örneğin, Circle sınıfı, draw () ve move () ve resize () için sanal işlevlere sahipse, bir gazillion Circle nesnesi olsa bile Circle sınıfıyla ilişkilendirilmiş tam olarak bir v-tablosu ve bu Circle nesnelerinin her biri Circle v-tablosuna işaret eder. V-tablosunun kendisi sınıftaki sanal işlevlerin her birine işaret eder. Örneğin, Circle v-tablosunda üç işaretçi bulunur: Circle :: draw () öğesine bir işaretçi, Circle :: move () öğesine bir işaretçi ve Circle :: resize () öğesine bir işaretçi.

Bir sanal işlevin gönderilmesi sırasında, çalışma zamanı sistemi nesnenin v-işaretçisini sınıfın v-tablosuna, ardından v-tablosundaki yöntem yuvasına uygun yuvayı takip eder.

Yukarıdaki tekniğin alan maliyeti ek yükü nominaldir: nesne başına fazladan bir işaretçi (ancak yalnızca dinamik bağlama yapması gereken nesneler için) ve yöntem başına fazladan bir işaretçi (ancak yalnızca sanal yöntemler için). Zaman maliyeti ek yükü de oldukça nominaldir: normal bir işlev çağrısına kıyasla, sanal bir işlev çağrısı iki ekstra getirme gerektirir (biri v-işaretçisinin değerini almak için bir, yöntemin adresini almak için bir saniye). Derleyici sanal olmayan işlevleri yalnızca işaretçinin türüne göre derleme zamanında çözdüğü için bu çalışma zamanı etkinliğinin hiçbiri sanal olmayan işlevlerde gerçekleşmez.


Sorunum ya da buraya nasıl geldim

Farklı tür küpler (bazıları piksel, bazıları görüntü, vb.) İçin farklı uygulanacak templated optimize yük fonksiyonları ile bir küp dosya taban sınıfı için şimdi böyle bir şey kullanmaya çalışıyorum.

Bazı kodlar:

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;

Ne olmasını isterdim, ancak sanal templated combo nedeniyle derlenmeyecek:

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;

Şablon bildirimini sınıf seviyesine taşıdım . Bu çözüm, programları okumadan önce okuyacakları belirli veri türlerini öğrenmeye zorlardı, bu kabul edilemez.

Çözüm

uyarı, bu çok hoş değil ama tekrarlayan yürütme kodunu kaldırmama izin verdi

1) temel sınıfta

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) ve çocuk sınıflarında

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

LoadAnyCube öğesinin temel sınıfta bildirilmediğini unutmayın.


İşte geçici çözüm içeren başka bir yığın taşması yanıtı: sanal şablon üyesi geçici çözümüne ihtiyacınız var .


1
Aynı durumla ve kitle sınıflarının kalıtım yapısıyla tanıştım. Makrolar yardımcı oldu.
ZFY

16

Aşağıdaki kod, Pencere 7'de MinGW G ++ 3.4.5 kullanılarak derlenebilir ve düzgün çalışır:

#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);
}

ve çıktı:

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

Ve sonra yeni bir X sınıfı ekledim:

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

Main () sınıfı X kullanmaya şu şekilde çalıştığımda:

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

g ++ aşağıdaki hatayı bildirin:

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

Yani belli ki:

  • sanal üye işlevi bir sınıf şablonunda kullanılabilir. Derleyicinin vtable oluşturması kolaydır
  • Gördüğünüz gibi, bir sınıf şablonu üye işlevini sanal olarak tanımlamak imkansızdır, işlev imzasını belirlemek ve vtable girişleri tahsis etmek zordur.

19
Sınıf şablonunun sanal üye işlevleri olabilir. Bir üye işlevi hem üye işlev şablonu hem de sanal üye işlevi olmayabilir.
James McNellis

1
aslında gcc ile başarısız oluyor 4.4.3. Eminim sistemimde Ubuntu 10.04
blueskin

3
Bu, sorunun sorduğundan tamamen farklıdır. Burada tüm temel sınıf şablonlaştırılmıştır. Daha önce bu tür şeyleri derledim. Bu da Visual Studio 2010'da derlenecek
ds-bos-msk

14

Hayır yapamazlar. Fakat:

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);
};

tek yapmanız gereken ortak bir arayüze sahip olmak ve uygulamayı alt sınıflara ertelemekse aynı etkiye sahiptir.


3
Merak eden biri varsa buna CRTP denir.
Michael Choi

1
Ancak bu, bir sınıf hiyerarşisine sahip olan ve temel sınıflara sanal işaretçi yöntemlerini çağırmak isteyen durumlarda işe yaramaz. Kişisel Fooolarak işaretçi nitelikli Foo<Bar>bir işaret edemez Foo<Barf>ya Foo<XXX>.
Kai Petzke

@KaiPetzke: Kısıtsız bir işaretçi oluşturamazsınız, hayır. Ancak, aynı etkiye sahip olan (kavramsal olarak en azından - açıkçası tamamen farklı bir uygulama) beton türünü bilmesine gerek olmayan herhangi bir kodu şablonlayabilirsiniz.
Tom

8

Hayır, şablon üyesi işlevleri sanal olamaz.


9
Merakım: Neden? Derleyici bunu yaparken ne gibi sorunlarla karşılaşıyor?
WannaBeGeek

1
Kapsamda bir bildirime ihtiyacınız var (en azından türleri doğru yapmak için). Standart (ve dil) tarafından, kullandığınız tanımlayıcılar için bir kapsam beyanı olması gerekir.
dirkgently

4

Diğer cevaplarda önerilen şablon fonksiyonu bir cephedir ve herhangi bir pratik fayda sağlamaz.

  • Şablon işlevleri, farklı türleri kullanarak yalnızca bir kez kod yazmak için kullanışlıdır.
  • Sanal fonksiyonlar, farklı sınıflar için ortak bir arayüze sahip olmak için kullanışlıdır.

Dil, sanal şablon işlevlerine izin vermez, ancak geçici bir çözümle, örneğin her sınıf için bir şablon uygulaması ve bir sanal ortak arabirime sahip olmak mümkündür.

Bununla birlikte, her bir şablon türü kombinasyonu için bir kukla sanal sarma işlevi tanımlamak gerekir:

#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;
}

Çıktı:

Kare alan 1, Daire alan 3.1415926535897932385

Burada deneyin


3

Sorunun ikinci bölümünü cevaplamak için:

Sanal olabilirlerse, böyle bir işlevi kullanacağı bir senaryo örneği nedir?

Bu yapmak mantıksız bir şey değil. Örneğin, Java'nın (her yöntemin sanal olduğu) genel yöntemlerle bir sorunu yoktur.

C ++ 'da sanal işlev şablonu istemenin bir örneği, genel bir yineleyiciyi kabul eden bir üye işlevidir. Veya genel işlev nesnesini kabul eden bir üye işlev.

Bu sorunun çözümü, fonksiyonunuzu bir şablon haline getirmeye gerek kalmadan genel bir yineleyici veya functor kabul etmenizi sağlayacak boost :: any_range ve boost :: fonksiyonuyla tür silme yöntemini kullanmaktır.


6
Java jenerikleri döküm için sözdizimsel şekerdir. Bunlar şablonlarla aynı değildir.
Brice M. Dempsey

2
@ BriceM.Dempsey: Dökümün Java'nın tersi olmak yerine jenerikleri uygulama biçimi olduğunu söyleyebilirsin ... ve matematiksel olarak, kullanım örneği exclipy hediyeleri geçerli IMO'dur.
einpoklum

2

Şablon yöntemi için tür kümesi önceden biliniyorsa, 'sanal şablon yöntemi' için bir geçici çözüm vardır.

Fikri göstermek için, aşağıdaki örnekte sadece iki tür kullanılmaktadır ( intve double).

Burada, bir 'sanal' şablon yöntemi ( Base::Method) karşılık gelen sanal yöntemi (bunlardan biri Base::VMethod) çağırır, bu da şablon yöntemi uygulamasını ( Impl::TMethod) çağırır .

Şablon türünün TMethodsadece türetilmiş uygulamalarda ( AImpl, BImpl) uygulanması ve kullanılması gerekir 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;
}

Çıktı:

0
1
2
3

Not: Base::Methodaslında gerçek kod fazlasıdır ( VMethodkamuya açık yapılabilir ve doğrudan kullanılabilir). Ekledim, böylece gerçek bir 'sanal' şablon yöntemi gibi görünüyor.


İş yerinde bir problem çözerken bu çözümü buldum. Mark Essel'in yukarıdaki ile benzer görünmesine rağmen, umarım daha iyi uygulanır ve açıklanır.
sad1raf

Bunu zaten kod gizleme olarak nitelendiririm ve hala Base, şimdiye kadar uygulanmış olanlarla uyumlu olmayan bir argüman türüyle bir şablon işlevini çağırmanız gerektiğinde orijinal sınıfı değiştirmek zorunda kalmazsınız . Bu zorunluluktan kaçınmak şablonların
Aconcagua

Essels yaklaşımı tamamen farklıdır: Farklı şablon örneklerini kabul eden sıradan sanal işlevler - ve türetilmiş sınıftaki son şablon işlevi yalnızca kod yinelemesini önlemeye hizmet eder ve temel sınıfta bir karşı parçası bile yoktur ...
Aconcagua

2

Birçok kişi tarafından cevaplanan daha eski bir soru, gönderilen diğerlerinden çok farklı olmayan kısa ve öz bir yöntem olduğuna inanırken, sınıf bildirimlerinin çoğaltılmasını kolaylaştırmak için küçük bir makro kullanmaktır.

// 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;
    // ...
};

Şimdi, alt sınıfımızı uygulamak için:

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
};

Buradaki fayda, yeni desteklenen bir tür eklerken, tümünün soyut üstbilgiden yapılabileceği ve muhtemelen birden çok kaynak / başlık dosyasında düzeltilebileceğidir.


0

En azından gcc 5.4 ile sanal işlevler şablon üyeleri olabilir, ancak şablonların kendileri olmalıdır.

#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;
}

çıktılar

mix before a2
Process finished with exit code 0

0

Bunu dene:

Classeder.h yazınız:

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);
    }

Bununla çalışıyorsanız, main.cpp'de bu kodu yazmak için kontrol edin:

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

int main()
{
     Example exmpl;
     exmpl.c_value = "Hello, world!";
     std::cout << exmpl.VirtualFunc(exmpl);
     return 0;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.