例えば
template <class T1, class T2>
class foo
{
T1 t1;
T2 t2;
T1 bar(); //Always exists
decltype(t1(t2)) baz(); //Should only exist if t1(t2) is valid
};
が無効な場合baz
でも、実際に誰も呼び出さない限り、プログラムをコンパイルしたいと考えていますbaz
。
baz
戻り値の型が無効な場合、ハード エラーが発生するのではなく、メンバーが SFINAE で出力されるように、テンプレートを作成できます。
template <class T1, class T2>
class foo
{
T1 t1;
T2 t2;
T1 bar(); //Always exists
template<typename T = T1>
decltype(std::declval<T&>()(t2)) baz();
};
このT
パラメーターは、計算式をタイプ依存にするために必要です。そうしないと、SFINAE は適用されません。この実装の詳細が「漏れ」 f.baz<int>()
、誰かbaz
が. どちらのアプローチでも、メンバーのアドレスを取得するのが難しくなります。たとえば、 のようになります。template<typename... Dummy, typename T = T1>
static_assert( sizeof...(Dummy) == 0, "Incorrect usage" );
&foo<T, U>::baz<>
もう 1 つのアプローチは、クラス テンプレートの特殊化を導入することです。
template<typename...> struct void_ { using type = void; };
template<typename T1, typename T2, typename = void>
class foo {
// version without baz
};
template<typename T1, typename T2>
class foo<T1, T2, typename void_<decltype(std::declval<T1&>()(std::declval<T2>()))>::type> {
decltype(std::declval<T1&>()(std::declval<T2>())) baz();
};
この場合&foo<T, U>::baz
、メンバーのアドレスを取得するのに問題はありません (もちろん存在すると仮定します)。両方の特殊化に共通するコードは、共通のベースに分解できます。また、実装の詳細として導入された追加のテンプレート パラメータが漏れる心配がある場合は、テンプレート パラメータをfoo
2 つだけ取る「本物」を作成することができます。次に、そのような実装から継承します。