私は以下のコードがクラスの部分的な特殊化であることを知っています:
template <typename T1, typename T2>
class MyClass {
…
};
// partial specialization: both template parameters have same type
template <typename T>
class MyClass<T,T> {
…
};
また、C++では関数テンプレートの部分的な特殊化が許可されていないことも知っています(完全なもののみが許可されています)。しかし、私のコードは、関数テンプレートを1つ/同じ型の引数に部分的に特化したことを意味していますか? Microsoft Visual Studio 2010 Expressで機能するためです!いいえの場合、部分的な専門化の概念を説明してください。
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template <typename T1, typename T2>
inline T1 max (T1 const& a, T2 const& b)
{
return a < b ? b : a;
}
template <typename T>
inline T const& max (T const& a, T const& b)
{
return 10;
}
int main ()
{
cout << max(4,4.2) << endl;;
cout << max(5,5) << endl;
int z;
cin>>z;
}
この例では、実際にはオーバーロード(特殊化ではない) the max<T1,T2>
関数。部分的な特殊化syntaxはsomewhatのように見えるはずです(許可されていた):
//Partial specialization is not allowed by the spec, though!
template <typename T>
inline T const& max<T,T> (T const& a, T const& b)
{ ^^^^^ <--- specializing here
return 10;
}
[注:関数テンプレートの場合、fullspecializationはC++標準で許可されています(コンパイラ拡張機能を除く)。]
他の回答が指摘したように、部分的な特殊化は許可されていないため、std::is_same
およびstd::enable_if
、 以下のように:
template <typename T, class F>
inline typename std::enable_if<std::is_same<T, int>::value, void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with ints! " << f << std::endl;
}
template <typename T, class F>
inline typename std::enable_if<std::is_same<T, float>::value, void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with floats! " << f << std::endl;
}
int main(int argc, char *argv[]) {
typed_foo<int>("works");
typed_foo<float>(2);
}
出力:
$ ./a.out
>>> messing with ints! works
>>> messing with floats! 2
編集:残っている他のすべてのケースを処理できるようにする必要がある場合は、既に処理されたケースはmatch-そうでなければ、あいまいな定義に陥ります。定義は次のとおりです。
template <typename T, class F>
inline typename std::enable_if<(not std::is_same<T, int>::value)
and (not std::is_same<T, float>::value), void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with unknown stuff! " << f << std::endl;
}
int main(int argc, char *argv[]) {
typed_foo<int>("works");
typed_foo<float>(2);
typed_foo<std::string>("either");
}
生成するもの:
$ ./a.out
>>> messing with ints! works
>>> messing with floats! 2
>>> messing with unknown stuff! either
このall-casesのことは少し退屈に見えますが、すでに行ったことをすべてコンパイラに伝える必要があるため、 5つ以上の専門分野。
専門化とは?
テンプレートを本当に理解したい場合は、関数型言語をご覧ください。 C++のテンプレートの世界は、純粋に機能的な独自のサブ言語です。
関数型言語では、選択はPattern Matchingを使用して行われます。
-- An instance of Maybe is either nothing (None) or something (Just a)
-- where a is any type
data Maybe a = None | Just a
-- declare function isJust, which takes a Maybe
-- and checks whether it's None or Just
isJust :: Maybe a -> Bool
-- definition: two cases (_ is a wildcard)
isJust None = False
isJust Just _ = True
ご覧のとおり、isJust
の定義をoverloadします。
C++クラステンプレートはまったく同じように機能します。パラメーターの数と性質を示すmain宣言を提供します。それは単なる宣言であるか、定義(選択)としても機能し、(必要に応じて)パターンの特殊化を提供し、クラスの別の(そうでなければ愚かな)バージョンに関連付けることができます。
テンプレート関数の場合、特殊化はやや厄介です。それはオーバーロード解決といくらか矛盾します。そのため、特化は非特化バージョンに関連し、過負荷の解決中に特化は考慮されないことが決定されました。したがって、適切な関数を選択するためのアルゴリズムは次のようになります。
(詳細な処理については、 GotW#49 を参照)
そのため、関数のテンプレート特化は、セカンドゾーンの市民です(文字通り)。私が懸念している限り、それらがなくても良いでしょう。テンプレートの特殊化の使用がオーバーロードでは解決できない場合はまだありません。
これはテンプレートの専門ですか?
いいえ、それは単に過負荷であり、これは問題ありません。実際、通常、オーバーロードは期待どおりに機能しますが、特殊化は驚くべきことです(リンクしたGotWの記事を思い出してください)。
いいえ。たとえば、std::swap
、ただし、独自のオーバーロードを法的に定義することはできません。つまり、std::swap
独自のカスタムクラステンプレートで動作します。
オーバーロードと部分的な特殊化は、場合によっては同じ効果がありますが、すべてとはほど遠い場合があります。
非クラス、非変数の部分的な特殊化は許可されていませんが、前述のとおり:
コンピュータサイエンスのすべての問題は、別のレベルの間接参照によって解決できます。 -デヴィッド・ホイーラー
クラスを追加して関数呼び出しを転送すると、これを解決できます。例を次に示します。
template <class Tag, class R, class... Ts>
struct enable_fun_partial_spec;
struct fun_tag {};
template <class R, class... Ts>
constexpr R fun(Ts&&... ts) {
return enable_fun_partial_spec<fun_tag, R, Ts...>::call(
std::forward<Ts>(ts)...);
}
template <class R, class... Ts>
struct enable_fun_partial_spec<fun_tag, R, Ts...> {
constexpr static R call(Ts&&... ts) { return {0}; }
};
template <class R, class T>
struct enable_fun_partial_spec<fun_tag, R, T, T> {
constexpr static R call(T, T) { return {1}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, int> {
constexpr static R call(int, int) { return {2}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, char> {
constexpr static R call(int, char) { return {3}; }
};
template <class R, class T2>
struct enable_fun_partial_spec<fun_tag, R, char, T2> {
constexpr static R call(char, T2) { return {4}; }
};
static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, "");
static_assert(fun<int>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, "");
static_assert(fun<char>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, "");
static_assert(fun<long>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, "");
static_assert(fun<double>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, "");
static_assert(fun<int>(1u, 1) == 0, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, "");
static_assert(fun<char>(1, 'c') == 3, "");
static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, "");
static_assert(fun<unsigned>('c', 1) == 4, "");
static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, "");
static_assert(fun<unsigned>(10.0, 1) == 0, "");
static_assert(
std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, "");
static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, "");
static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, "");
static_assert(fun<unsigned>() == 0, "");
遅い答えですが、一部の遅い読者はそれを役に立つと思うかもしれません:時には、ヘルパー関数-特殊化できるように設計されている-が問題を解決することもできます。
だから、想像してみましょう、これが私たちが解決しようとした:
template <typename R, typename X, typename Y>
void function(X x, Y y)
{
R* r = new R(x);
f(r, y); // another template function?
}
// for some reason, we NEED the specialization:
template <typename R, typename Y>
void function<R, int, Y>(int x, Y y)
{
// unfortunately, Wrapper has no constructor accepting int:
Wrapper* w = new Wrapper();
w->setValue(x);
f(w, y);
}
OK、部分的なテンプレート関数の特殊化、それはできません...それでは、特殊化に必要な部分をヘルパー関数に「エクスポート」して、それを特殊化して使用しましょう。
template <typename R, typename T>
R* create(T t)
{
return new R(t);
}
template <>
Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal...
{
Wrapper* w = new Wrapper();
w->setValue(n);
return w;
}
template <typename R, typename X, typename Y>
void function(X x, Y y)
{
R* r = create<R>(x);
f(r, y); // another template function?
}
このは、特に代替手段(特殊化の代わりに通常のオーバーロード、 回避策 ルーベンスによって提案された場合、...これらが悪いことや私のほうが良いということではなく、ちょうどanotherone)はかなり多くの共通コードを共有します。