Überladen einer Funktion mit Vorlagen

34

Ich versuche, eine Funktion mithilfe von Vorlagen zu definieren, und möchte, dass der Typname entweder int oder anEnum ist (eine bestimmte Aufzählung, die ich definiert habe). Ich habe Folgendes versucht, bin aber gescheitert:

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

Ich versuche, Vorlagen zu verwenden, anstatt zwei überladene Funktionen zu definieren. Ich würde es vorziehen, wenn die Funktion wie folgt aufgerufen würde, ohne dass der Programmierer den Typ berücksichtigen müsste

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

Grundsätzlich möchte ich, dass diese Funktion für int- und aNum-Typen als Vorlage verwendet wird. Ich habe danach gesucht, konnte aber keine Antwort finden. Was kann ich vermissen? Vielen Dank,

bg
quelle
Wenn es sich genau um eine einzelne Aufzählung oder den Typ int handelt, warum nicht einfach beide Funktionen schreiben? Warum brauchen Sie in diesem Fall eine Vorlage?
Klaus
Was ist mit anderen Typen? Möchten Sie falsefür andere Typen zurückkehren oder die Funktion für andere Typen nicht instanziieren ?
Frogatto
@frogatto Nein, der Bool-Rückgabewert hat nichts mit Typen.
bg
@Klaus Ich habe darum gebeten, Alternativen zu lernen. Aufgrund der aktuellen Antworten habe ich beschlossen, einfach beide Funktionen zu definieren.
bg

Antworten:

25

Zusätzlich zur Nicht-C ++ 20-Antwort würde ich Ihnen die folgende Implementierung vorschlagen , wenn Sie zufällig in der Lage sind, C ++ 20 und seine conceptsFunktion zu verwenden :

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

Demo

AKTUALISIEREN

Laut dem Kommentar von @RichardSmith gibt es hier einen skalierbareren und wiederverwendbaren Ansatz:

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;
}
Nussknacker
quelle
Für den speziellen Fall, dass der Typ einer von zwei spezifischen Typen sein muss, könnte so etwas besser funktionieren: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 Ich habe meine Antwort auch damit aktualisiert. Ich finde das wiederverwendbarer und skalierbarer. Danke
NutCracker
21

Es gibt verschiedene Möglichkeiten, dies zu erreichen. Alle beinhalten die Verwendung des type_traitsHeaders. Sie können beispielsweise die fraglichen Typen im Hauptteil der Funktion statisch bestätigen.

Wenn Sie diese Funktion unter anderen Überlastungen berücksichtigen müssen, kann eine SFINAE-Technik verwendet werden.

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

Dadurch wird die Funktion aus einem Überlastungssatz entfernt, bevor sie aufgerufen wird, wenn die Typen nicht übereinstimmen. Wenn Sie dieses Verhalten jedoch nicht benötigen, ermöglicht eine statische Zusicherung eine programmiererfreundlichere Fehlermeldung.

Geschichtenerzähler - Unslander Monica
quelle
3

Was ist mit dieser Lösung? Ein Code mit der Funktion wird kompiliert, wenn der Typ T Ihren Anforderungen entspricht. Andernfalls ist die statische Zusicherung fehlgeschlagen.

#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;
}
ixjxk
quelle
1
Dies funktioniert bei Überlastungsauflösung nicht gut, wenn andere Signaturen vorhanden sind (z. B. eine Hypothese isFunction(std::string_view)). Die Signatur ist weiterhin eine gültige Übereinstimmung, aber die Instanziierung verursacht einen Fehler.
LF
Sie können nutzlose Signaturen als gelöscht deklarieren: bool isFunction (std :: string_view) = delete;
ixjxk
Ich spreche von zusätzlichen Überlastungen. In diesem Fall kann diese ungültige Signatur eine exakte Übereinstimmung sein (z. B. für Zeichenfolgenliterale), wodurch die Überladung blockiert wird.
LF
0

Ich habe https://stackoverflow.com/a/60271100/12894563 verbessert Antwort auf . 'If constexpr' kann in dieser Situation helfen:

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) schlägt fehl, da keine überladene Funktion oder ein Zweig 'if constexpr' vorhanden ist.

UPDATE: Fehler behoben

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

https://godbolt.org/z/eh4pVn

ixjxk
quelle
static_assert(false, ...)ist schlecht geformter NDR, ohne überhaupt verwendet zu werden. Wenn Sie Glück haben, wird Ihr Compiler Ihnen sofort mitteilen, wie es Clang tut: godbolt.org/z/m_Gk9n
StoryTeller - Unslander Monica
Vielen Dank für Ihren Kommentar, ich habe einen Fehler gemacht. Behoben
ixjxk