Sovraccarico di una funzione mediante modelli


34

Sto provando a definire una funzione usando i template e voglio che il typename sia int o anEnum (un enum specifico che avevo definito). Ho provato quanto segue ma ho fallito:

template <int | anEnum T> // or <int T, anEnum T> or <int, anEnum T>
bool isFunction(const T &aVariable){}

Quello che sto cercando di fare è usare i template, invece di definire due funzioni sovraccaricate. Preferirei che la funzione fosse chiamata come segue, senza che il programmatore debba considerare il tipo

isFunction(aVariable) // and not isFunction<int> (aVariable) nor isFunction<anEnum> (aVariable)

Fondamentalmente, voglio che questa funzione sia modellata per i tipi int e aNum. Ho cercato questo, ma non sono riuscito a trovare la risposta. Cosa posso perdere? Grazie,


Se è esattamente un singolo enum o il tipo int, perché non scrivere semplicemente entrambe le funzioni? Perché hai bisogno di un modello in quel caso?
Klaus

E gli altri tipi? Vuoi tornare falseper altri tipi o non creare un'istanza della funzione per altri tipi.
frogatto,

@frogatto No, il valore di ritorno bool non ha nulla con i tipi.
bg

@Klaus Ho chiesto di imparare alternative. Sulla base delle risposte attuali, ho deciso di definire semplicemente entrambe le funzioni.
bg

Risposte:


25

Oltre alla risposta non C ++ 20, se per caso sei in grado di utilizzare C ++ 20 e le sue conceptsfunzionalità, ti suggerirei la seguente implementazione:

#include <iostream>
#include <concepts>

enum class MyEnum {
    A,
    B,
    C
};

template <typename T>
concept IntegralOrEnum = std::same_as<MyEnum, T> || std::integral<T>;

template <IntegralOrEnum T>
bool isFunction(T const& aVariable) {
    return true;
}

int main() {
    isFunction(MyEnum::A);
    isFunction(3);
    isFunction("my_string"); // error
    return 0;
}

dimostrazione

AGGIORNARE

Secondo il commento di @RichardSmith , ecco un approccio più scalabile e riutilizzabile:

template <typename T, typename ...U>
concept one_of = (std::is_same_v<T, U> || ...);

template <one_of<int, MyEnum> T>
bool isFunction(T const& aVariable) {
    return true;
}

Per il caso specifico in cui si richiede che il tipo sia uno di due tipi specifici, qualcosa del genere potrebbe funzionare meglio:template<typename T, typename ...U> concept one_of = (std::is_same_v<T, U> || ...); template<one_of<int, MyEnum> T> bool isFunction(T const& aVariable) {
Richard Smith,

1
@RichardSmith Ho aggiornato anche la mia risposta. Lo trovo più riutilizzabile e scalabile. Grazie
Schiaccianoci

21

Ci sono un paio di modi per farlo. Tutti implicano l'uso type_traitsdell'intestazione. È possibile affermare staticamente i tipi in questione nel corpo della funzione, ad esempio.

Oppure, se è necessario considerare questa funzione tra gli altri sovraccarichi, è possibile utilizzare una tecnica SFINAE.

template<typename T>
auto isFunction(const T &aVariable) 
  -> std::enable_if_t<std::is_same<T, int>::value || std::is_same<T,anEnum>::value, bool> {
}

Questo rimuoverà la funzione da un set di sovraccarico prima che venga chiamato se i tipi non corrispondono. Ma se questo comportamento non è necessario, un'asserzione statica consente un messaggio di errore più intuitivo per il programmatore.


3

E questa soluzione? Un codice con la funzione verrà compilato se il tipo T è soddisfatto delle tue richieste. In caso contrario, l'asserzione statica non è riuscita.

#include <type_traits>
enum anEnum {
    //
};

template <typename T, bool defined = std::is_same<T, int>::value ||
                                     std::is_same<T, anEnum>::value>
bool isFunction(const T& aVariable)
{
    static_assert(defined, "Invalid specialization");

    bool result = false;
    // Put your code here
    return result;
}

1
Questo non funziona bene con la risoluzione del sovraccarico se sono presenti altre firme (ad esempio, un ipotetico isFunction(std::string_view)). La firma sarà comunque una corrispondenza valida, ma l'istanza provoca errori.
LF

Puoi dichiarare le firme inutili come cancellate: bool isFunction (std :: string_view) = delete;
ixjxk,

Sto parlando di sovraccarichi aggiuntivi. In tal caso, questa firma non valida potrebbe finire per essere una corrispondenza esatta (ad esempio, per i letterali di stringa), bloccando così il sovraccarico.
LF

0

Ho migliorato la risposta https://stackoverflow.com/a/60271100/12894563 . 'Se constexpr' può aiutare in questa situazione:

template <typename T>
struct always_false : std::false_type {};

template <typename T>
bool isFunction(const T& aVariable)
{
    if constexpr(std::is_same_v<T, int> || std::is_same_v<T, anEnum>)
    {
        std::cout << "int\n";
        // put your code here
        return true;
    }
    else
    {
        static_assert(always_false<T>::value, "You should declare non-template function or write if constexpr branch for your type");
        return false;
    }
}

bool isFunction(std::string_view)
{
    std::cout << "std::string_view\n";
    return true;
}

int main()
{
    isFunction(std::string_view("1L"));
    isFunction(1);
    //isFunction(1L); // will produce an error message from static_assert
}

isFunction (1L) non riuscirà a causa della mancanza di una funzione sovraccarica o del ramo 'if constexpr'.

AGGIORNAMENTO: risolto mancato

template <typename T>
struct always_false : std::false_type {};

https://godbolt.org/z/eh4pVn


static_assert(false, ...)è un rapporto di mancato recapito mal formato, senza nemmeno essere utilizzato. Se siete fortunati, il compilatore vi dirà subito, come Clang fa, godbolt.org/z/m_Gk9n
Storyteller - Unslander Monica

Grazie mille per il tuo commento, ho fatto un errore. Risolto, godbolt.org/z/eh4pVn
ixjxk il
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.