4

可変個引数クラステンプレートderivから派生した可変個引数クラステンプレートがありbaseます。

任意の型をとる関数テンプレートと、型Tのオーバーロードがbase<Ts...>あります。

base<Ts...>を渡すときに使用されるオーバーロードを取得するにはどうすればよいconst deriv<Ts...>&ですか?

以下の作業例:

#include <iostream>
#include <tuple>

template<typename... Ts>
struct base
{
    std::tuple<Ts...> tuple;
};

template<typename... Ts>
struct deriv : base<Ts...>
{
};

//--------------------------------

template<typename T>
void func(const T&)
{
    std::cout << "T" << std::endl;
}

template<typename... Ts>
void func(const base<Ts...>&)
{
    std::cout << "base<Ts...>" << std::endl;
}

//----------------------------------------

int main()
{
    int a;
    base <int, double> b;
    deriv<int, double> c;

    func(a);
    func(b);
    func(c); // <--- I want func<base<Ts...>> not func<T> to be called here

    exit(0);
}

エグザンプラからの出力:

T
base<Ts...>
T

私が出力したいもの:

T
base<Ts...>
base<Ts...>
4

2 に答える 2

6

コードを再設計する準備ができていない限り、それはできません。それには正当な理由があります。

の非可変個引数のオーバーロードはfunc()、可変個引数バージョンよりもよく一致します。実際、関数呼び出しを解決しようとするとT、非可変個引数のオーバーロードの型パラメーターはであると推定されますderived<int, double>

一方、Ts可変個引数のオーバーロードのパラメータパックはであると推定されますint, double。型の推定後、これにより、呼び出しを解決するための2つの選択肢がコンパイラーに実質的に残ります。

void func(const deriv<int, double>&); // Non-variadic after type deduction
void func(const base<int, double>&);    // Variadic after type deduction

引数がタイプの呼び出しに一致させようとするときに、どれを選択する必要がありますderived<int, double>か?

deriv<int, double> c;
func(c);

明らかに、最初の非可変個引数の過負荷がより適切に一致します。

では、最初のオーバーロードの代わりに2番目のオーバーロードをどのように呼び出しますか?いくつかの選択肢があります。まず、テンプレート引数を明示的に指定することで、呼び出しを修飾できます。

func<int, double>(c);

それが気に入らない場合は、次の非可変個引数のオーバーロードの定義を再考することができます:可能タイプfunc()を本当に受け入れたいですか?または、このオーバーロードが呼び出されないことがわかっているタイプがいくつかありますか?その場合は、SFINAE手法を使用して、不要な一致を除外できます。Tstd::enable_if

さらなる可能性として、テンプレート関数のシグネチャを少し緩和して、特定のテンプレートクラスのインスタンス化としてその引数を推測できるようにすることができます。

template<template<typename...> class T, typename... Ts>
void func(const T<Ts...>&)
{
    std::cout << "base<Ts...>" << std::endl;
}

この変更だけで、プログラムの動作を希望どおりに修正できます。

アップデート:

特殊な関数テンプレートをクラステンプレートの任意のインスタンスから派生したクラスに対してのみ呼び出す場合は、型特性を次のようにbase<>使用できます。std::is_base_of<>std::enable_if

template<template<typename...> class T, typename... Ts>
void func(
    const T<Ts...>&, 
    typename std::enable_if<
        std::is_base_of<base<Ts...>, T<Ts...>>::value
        >::type* = nullptr
    )
{
    std::cout << "base<Ts...>" << std::endl;
}

補遺:

テンプレート関数のオーバーロードが設計に役立たない状況では、いつでも部分的なテンプレートの特殊化に頼ることができることに注意してください。残念ながら、関数テンプレートを特殊化することはできませんが、クラステンプレートの部分的な特殊化を活用し、ヘルパー関数を追加してそのテンプレートのインスタンス化を非表示にすることはできます。これはあなたがあなたのコードを書き直す方法です:

namespace detail
{
    template<typename T>
    struct X
    {
        static void func(const T&)
        {
            std::cout << "T" << std::endl;
        }
    };

    template<template<typename...> class T, typename... Ts>
    struct X<T<Ts...>>
    {
        static void func(const T<Ts...>&)
        {
            std::cout << "base<Ts...>" << std::endl;
        }
    };
}

template<typename T>
void func(const T& t)
{
    details::X<T>::func(t);
}
于 2013-02-03T23:42:27.037 に答える
0

ジェネリックテンプレートのオーバーロードは、変換を必要としないため、より適切に一致します(const両方のオーバーロードにある追加を除く)。

base明示的なキャストを追加することで、-templateのオーバーロードを取得できます()。

func(static_cast<base<int, double> &>(c));

(または、複数のオーバーロードを省略して、代わりにいくつかのis_base_ofヘルパーロジックをメイン関数テンプレートの本体に貼り付けることもできます。)

于 2013-02-03T23:43:49.830 に答える