テンプレートを使用した関数のオーバーロード


34

テンプレートを使用して関数を定義しようとしています。型名をintまたはanEnum(定義した特定の列挙型)にしたいのですが。私は以下を試しましたが失敗しました:

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

私がやろうとしていることは、2つのオーバーロードされた関数を定義する代わりに、テンプレートを使用することです。プログラマがタイプを考慮する必要なしに、次のように関数を呼び出す方がいいです

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

基本的に、この関数をintおよびaNumタイプ用にテンプレート化します。私はこれを検索しましたが、答えが見つかりませんでした。何が欠けているのでしょうか?ありがとうございました、


それが単一の列挙型またはint型である場合は、両方の関数を単純に記述しないのはなぜですか?その場合、なぜテンプレートが必要なのですか?
クラウス

他のタイプはどうですか?false他の型に戻りたいですか、それとも他の型の関数をインスタンス化しないか。
frogatto

@frogattoいいえ、boolの戻り値には型に関するものはありません。
bg

@クラウス私は代替案を学ぶように頼んだ。現在の回答に基づいて、両方の関数を単純に定義することにしました。
bg

回答:


25

C ++ 20以外の回答に加えて、万が一、C ++ 20とそのconcepts機能を使用できる場合は、次の実装をお勧めします。

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

デモ

更新

@RichardSmithのコメントによると、これはよりスケーラブルで再利用可能なアプローチです:

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

:2つの特定の種類の、より良いかもしれない。この作品のようなものの一つとタイプを必要とする特定のケースの場合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) {
リチャード・スミス

1
@RichardSmith私もそれで私の答えを更新しました。これは再利用可能でスケーラブルだと思います。ありがとう
NutCracker

21

これを行うにはいくつかの方法があります。すべてtype_traitsヘッダーを使用する必要があります。たとえば、関数の本体で問題の型に対して静的にアサートできます。

または、他のオーバーロードの中でこの関数を検討する必要がある場合は、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> {
}

これにより、型が一致しない場合に呼び出される前に、オーバーロードセットから関数が削除されます。ただし、この動作が必要ない場合は、静的アサーションを使用すると、よりプログラマーフレンドリーなエラーメッセージを表示できます。


3

このソリューションはどうですか?タイプTが要件を満たしている場合、関数を含むコードがコンパイルされます。それ以外の場合、静的アサーションは失敗しました。

#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
これは、他のシグネチャが存在する場合(たとえば、架空のisFunction(std::string_view))、オーバーロード解決ではうまく機能しません。署名は依然として有効な一致ですが、インスタンス化によりエラーが発生します。
LF

不要なシグネチャを削除済みとして宣言できます。bool isFunction(std :: string_view)= delete;
ixjxk

追加のオーバーロードについて話している。その場合、この無効なシグネチャは完全一致(文字列リテラルなど)になる可能性があり、過負荷をブロックします。
LF

0

https://stackoverflow.com/a/60271100/12894563の回答を改善しました。「constexpr」がこの状況で役立つ場合:

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)は、オーバーロードされた関数や 'if constexpr'ブランチがないため失敗します。

更新:欠落した修正

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

https://godbolt.org/z/eh4pVn


static_assert(false, ...)使用されていない、不正なNDRです。運が良ければ
z / m_Gk9n

コメントありがとうございました。間違えました。修正済み、godbolt.org
z
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.