Esegue la funzione nel modello di funzione solo per quei tipi che hanno la funzione definita


13

Ho un modello di funzione che accetta molti tipi diversi come input. Di questi tipi solo uno ha una getInt()funzione. Quindi voglio che il codice esegua la funzione solo per quel tipo. Si prega di suggerire una soluzione. Grazie

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

Non correlato al tuo problema, ma la type_infostruttura ha un operatore di confronto di uguaglianza , quindi typeid(T) == typeid(X)dovrebbe funzionare anche.
Qualche programmatore amico

5
Usa: if constexprcon condizioni is_same_v<T,X>.
rafix07,

La soluzione a questo diventerà ufficialmente più elegante entro la fine dell'anno con Concepts. Non molto utile in questo momento, lo so.
svedese il

Esistono molti modi per risolvere il tuo problema. Una coppia di cui sopra. Puoi anche usare tratti di diverse varianti per vedere se un tipo ha un getIntmembro richiamabile . Devono esserci alcune domande qui solo su stackoverflow.com su come vedere se una struttura o classe ha una funzione membro specifica, se cerchi solo un po '.
Qualche programmatore amico

Risposte:


10

Se vuoi essere in grado di chiamare una funzione fper tutti i tipi che hanno un membro di funzione getInt, non solo X, puoi dichiarare 2 sovraccarichi per la funzione f:

  1. per i tipi con getIntfunzione membro, inclusa la classeX

  2. per tutti gli altri tipi, compresa la classe Y.

Soluzione C ++ 11 / C ++ 17

Tenendo presente ciò, potresti fare qualcosa del genere:

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

Dai un'occhiata dal vivo .

Si noti che std::void_tè stato introdotto in C ++ 17, ma se si è limitati a C ++ 11, è davvero facile implementarlo void_tda soli:

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

Ed ecco la versione C ++ 11 live .

Cosa abbiamo in C ++ 20?

C ++ 20 porta molte cose buone e uno di questi è concetti . Inoltre, ciò che è valido per C ++ 11 / C ++ 14 / C ++ 17 può essere significativamente ridotto in C ++ 20:

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

Dai un'occhiata dal vivo .


Prima di C ++ 17, l'implementazione delle void_tcause causa problemi a qualche vecchio compilatore (come indicato dal collegamento).
Jarod42

non è strettamente necessario scrivere due sovraccarichi (sostituire "bisogno" con "can" sarebbe molto meglio imho)
idclev 463035818

@ idclev463035818 aggiornato. Grazie
NutCracker

1
@SSAnne aggiornato
NutCracker

1
La definizione del concetto non è accurata. stai assegnando il risultato a un int quindi il concetto dovrebbe esseretemplate<typename T> concept HasGetInt = requires (T& v) { {v.getInt()} -> std::convertible_to<int>; };
Hui,

8

È possibile utilizzare if constexprda C ++ 17:

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

Prima, dovrai usare sovraccarichi e SFINAE o invio di tag.


if constexprè una funzionalità di C ++ 17.
Andrey Semashev,

Tuttavia, questo funzionerebbe solo per la classeX
NutCracker

La domanda è ora aggiornata solo a C ++ 11 / C ++ 14
NutCracker

@NutCracker: non è bello aggiornare tag / domanda e quindi invalidare le risposte esistenti ... (anche se l'avvertimento è corretto).
Jarod42

ho appena aggiornato il tag ... il titolo della domanda è stato aggiornato da OP
NutCracker il

7

Mantenerlo semplice e sovraccarico. Funziona da almeno C ++ 98 ...

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

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

Questo è sufficiente se esiste un solo tipo con getIntfunzione. Se c'è di più, non è più così semplice. Esistono diversi modi per farlo, eccone uno:

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
}

Esempio live con output diagnostico.


1
In questo caso ciò funzionerebbe poiché esiste un solo sovraccarico (per X), ma, se getIntin futuro ci fossero più tipi simili di membri , questa non è una buona pratica. Probabilmente vuoi notare che
NutCracker

@NutCracker Lo ha fatto.
jrok
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.