あなたはまだより良い答えを待っているとあなたが言ったので、これが私の見解です。完璧ではありませんが、SFINAEと部分的なスペシャライゼーションを使用することで可能な限り可能になると思います。(Conceptsは完全でエレガントなソリューションを提供すると思いますが、それをもう少し待つ必要があります。)
このソリューションは、C ++ 14の最終バージョン以降の標準作業ドラフトで最近指定されたばかりのエイリアステンプレートの機能に依存していますが、しばらくの間、実装によってサポートされています。ドラフトN4527[14.5.7p3]の関連する文言は次のとおりです。
ただし、template-idが依存している場合でも、後続のテンプレート引数の置換はtemplate-idに適用されます。[ 例:
template<typename...> using void_t = void;
template<typename T> void_t<typename T::foo> f();
f<int>(); // error, int does not have a nested type foo
—例を終了]
このアイデアを実装する完全な例を次に示します。
#include <iostream>
#include <type_traits>
#include <utility>
template<typename> struct User { static void f() { std::cout << "primary\n"; } };
template<typename> struct Data { };
template<typename T, typename U> struct Derived1 : Data<T*> { };
template<typename> struct Derived2 : Data<double> { };
struct DD : Data<int> { };
template<typename T> void take_data(Data<T>&&);
template<typename T, typename = decltype(take_data(std::declval<T>()))>
using enable_if_data = T;
template<template<typename...> class TT, typename... Ts>
struct User<enable_if_data<TT<Ts...>>>
{
static void f() { std::cout << "partial specialization for Data\n"; }
};
template<typename> struct Other { };
template<typename T> struct User<Other<T>>
{
static void f() { std::cout << "partial specialization for Other\n"; }
};
int main()
{
User<int>::f();
User<Data<int>>::f();
User<Derived1<int, long>>::f();
User<Derived2<char>>::f();
User<DD>::f();
User<Other<int>>::f();
}
それを実行すると、次のように出力されます。
primary
partial specialization for Data
partial specialization for Data
partial specialization for Data
primary
partial specialization for Other
ご覧のとおり、しわがあります。部分的な特殊化は選択されてDD
いません。また、宣言した方法のために選択できません。だから、私たちはただ言ってみませんか
template<typename T> struct User<enable_if_data<T>>
そしてそれも一致DD
させますか?これは実際にはGCCで機能しますが、[14.5.5p8.3、8.4]のためにClangとMSVCによって正しく拒否されます([p8.3]は冗長であるため、将来的になくなる可能性があります-CWG 2033):
- 特殊化の引数リストは、プライマリテンプレートの暗黙の引数リストと同一であってはなりません。
- スペシャライゼーションは、プライマリテンプレート(14.5.5.2)よりもスペシャライズされている必要があります。
User<enable_if_data<T>>
User<T>
(上記の最初の引用で説明されているように、個別に処理されるデフォルトの引数へのモジュロ置換)と同等であるため、部分的な特殊化の無効な形式です。残念ながら、のようなものを一致DD
させるには、一般に、フォームの部分的な特殊化引数が必要になりますT
-それが持つことができる他のフォームはなく、それでもすべてのケースに一致します。したがって、この部分は、与えられた制約内では解決できないと結論付けることができます。(1980年のコア問題があります。これは、テンプレートエイリアスの使用に関する将来のルールの可能性を示唆していますが、これらが私たちのケースを有効にするかどうかは疑問です。)
派生したクラス自体がテンプレートの特殊化である限り、Data<T>
上記の手法を使用してそれらをさらに制約することで機能するため、これが役立つことを願っています。
コンパイラのサポート(これは私がテストしたものです。他のバージョンでも機能する可能性があります):
- Clang 3.3-3.6.0、
-Wall -Wextra -std=c++11 -pedantic
-は上記のように機能します。
- GCC 4.7.3-4.9.2、同じオプション-上記と同じ。不思議なことに、GCC 5.1.0-5.2.0は、正しいバージョンのコードを使用して部分的な特殊化を選択しなくなりました。これは回帰のように見えます。適切なバグレポートをまとめる時間がありません。よろしければお気軽にどうぞ。この問題は、テンプレートテンプレートパラメータと一緒にパラメータパックを使用することに関連しているようです。とにかく、GCCはを使用して間違ったバージョンを受け入れる
enable_if_data<T>
ので、それは一時的な解決策になる可能性があります。
- MSVC:Visual C ++ 2015は、
/W4
上記のように機能します。古いバージョンはデフォルトの引数のを好みませんdecltype
が、テクニック自体は引き続き機能します。デフォルトの引数を制約を表現する別の方法に置き換えると、2013Update4で機能します。