İşlev şablonunun içindeki işlevi yalnızca işlevi tanımlanmış olan türler için yürüt


13

Ben girdi olarak birçok farklı türde alır bir işlev şablonu var. Bu tiplerden sadece bir tanesinin bir getInt()işlevi vardır. Bu nedenle, kodun yalnızca bu tür için işlevi çalıştırmasını istiyorum. Lütfen bir çözüm önerin. Teşekkürler

#include <type_traits>
#include <typeinfo>

class X {
    public:
    int getInt(){
        return 9;
    }
};

class Y{

};

template<typename T>
void f(T& v){
    // error: 'class Y' has no member named 'getInt'
    // also tried std::is_same<T, X>::value 
    if(typeid(T).name() == typeid(X).name()){
        int i = v.getInt();// I want this to be called for X only
    }
}

int main(){
    Y y;
    f(y);
}

Sorununuzla ilgisi yoktur, ancak type_infoyapının bir eşitlik karşılaştırma operatörü vardır , bu yüzden typeid(T) == typeid(X)de çalışmalıdır.
Bazı programcı ahbap

5
Kullanımı: if constexprkoşulu ile is_same_v<T,X>.
rafix07

Bunun çözümü, bu yıl Kavramlar ile resmen daha zarif hale gelecek. Şu anda çok yardımcı değil, biliyorum.
sweenish

Sorununuzu çözmenin birçok yolu vardır. Yukarıda bahsedilen bir çift. Bir türün çağrılabilir üyesi olup olmadığını görmek için farklı varyantların özelliklerini de kullanabilirsiniz getInt. Burada biraz arama yapıyorsanız, stackoverflow.com'da sadece bir yapının veya sınıfın belirli bir üye işlevinin olup olmadığını nasıl göreceğinizle ilgili oldukça birkaç soru olmalıdır.
Bazı programcı dostum

Yanıtlar:


10

Bir fişlevi getInt, yalnızca işlev değil , işlev üyesi olan tüm türler için çağırabilmek istiyorsanız, işlev için X2 aşırı yük bildirebilirsiniz f:

  1. getIntsınıf dahil üye işlevine sahip türler içinX

  2. sınıf dahil tüm diğer türler için Y.

C ++ 11 / C ++ 17 çözümü

Bunu aklınızda tutarak, böyle bir şey yapabilirsiniz:

#include <iostream>
#include <type_traits>

template <typename, typename = void>
struct has_getInt : std::false_type {};

template <typename T>
struct has_getInt<T, std::void_t<decltype(((T*)nullptr)->getInt())>> : std::is_convertible<decltype(((T*)nullptr)->getInt()), int>
{};

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T,
          typename std::enable_if<!has_getInt<T>::value, T>::type* = nullptr>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <typename T,
          typename std::enable_if<has_getInt<T>::value, T>::type* = nullptr>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

Canlı olarak kontrol edin .

std::void_tC ++ 17'de tanıtıldığını lütfen unutmayın , ancak C ++ 11 ile sınırlıysanız void_t, kendi başınıza uygulamak gerçekten kolaydır :

template <typename...>
using void_t = void;

Ve işte C ++ 11 sürümü canlı .

C ++ 20'de ne var?

C ++ 20 birçok iyi şey getirir ve bunlardan biri kavramlardır . C ++ 11 / C ++ 14 / C ++ 17 için geçerli olan yukarıdaki şey C ++ 20'de önemli ölçüde azaltılabilir:

#include <iostream>
#include <concepts>

template<typename T>
concept HasGetInt = requires (T& v) { { v.getInt() } -> std::convertible_to<int>; };

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <HasGetInt T>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

Canlı olarak kontrol edin .


C ++ 17'den önce, bu uygulamanın uygulanması void_tbazı eski derleyicilere neden olur (bağlantı tarafından işaret edildiği gibi).
Jarod42

iki aşırı yük yazmak kesinlikle gerekli değildir ("ihtiyaç" yerine "can" yerine çok daha iyi
imho olur

@ idclev463035818 güncellendi. Thanks
NutCracker

1
@SSAnne güncellendi
NutCracker

1
Kavram tanımı doğru değil. sonucu bir int atarsınız, bu yüzden konsept olmalıdırtemplate<typename T> concept HasGetInt = requires (T& v) { {v.getInt()} -> std::convertible_to<int>; };
Hui

8

if constexprC ++ 17'den kullanabilirsiniz :

template<typename T>
void f(T& v){
    if constexpr(std::is_same_v<T, X>) { // Or better create trait has_getInt
        int i = v.getInt();// I want this to be called for X only
    }
    // ...
}

Daha önce, aşırı yükleri ve SFINAE'yi veya etiket gönderimini kullanmanız gerekecek.


if constexprbir C ++ 17 özelliğidir.
Andrey Semashev

Ancak, bu sadece sınıf için çalışırX
NutCracker

Soru şimdi sadece C ++ 11 / C ++ 14
NutCracker

@NutCracker: Etiketi / soruyu güncellemek hoş değil ve bu nedenle mevcut cevapları geçersiz kılıyor ... (bu konuda uyarı iyi olsa bile).
Jarod42

i just updated tag ... soru başlığı OP
NutCracker

7

Basit ve aşırı yüklenmesini sağlayın. En az C ++ 98'den beri çalıştı ...

template<typename T>
void f(T& v)
{
    // do whatever
}

void f(X& v)
{
    int result = v.getInt();
}

getIntFonksiyonu olan tek bir tip varsa bu yeterlidir . Daha fazlası varsa, artık o kadar basit değil. Bunu yapmanın birkaç yolu var, işte bir tane:

struct PriorityA { };
struct PriorityB : PriorityA { };

template<typename T>
void f_impl(T& t, PriorityA)
{
    // generic version
}

// use expression SFINAE (-> decltype part)
// to enable/disable this overload
template<typename T>
auto f_impl(T& t, PriorityB) -> decltype(t.getInt(), void())
{
    t.getInt();
}

template<typename T>
void f(T& t)
{
    f_impl(t, PriorityB{ } ); // this will select PriorityB overload if it exists in overload set
                              // otherwise PriorityB gets sliced to PriorityA and calls generic version
}

Tanılama çıkışlı canlı örnek.


1
Bu durumda, bu sadece bir aşırı yük (çünkü X) olduğu için işe yarayacaktır , ancak getIntgelecekte üye ile daha fazla benzer tür varsa , bu iyi bir uygulama değildir. Muhtemelen bunu not etmek istersiniz
NutCracker

@NutCracker Öyle.
jrok
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.