コードを再設計する準備ができていない限り、それはできません。それには正当な理由があります。
の非可変個引数のオーバーロードは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手法を使用して、不要な一致を除外できます。T
std::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);
}