44

私がこれらの宣言を持っているとしましょう

template<typename T> class User;
template<typename T> class Data;

とから派生したクラスUser<>を実装したいだけでなく、他の場所で定義された他の特殊化も可能にします。T = Data<some_type> Data<some_type>

クラステンプレートの宣言がまだない場合は、User<>単純に

template<typename T,
         typename A= typename std::enable_if<is_Data<T>::value>::type>
class User { /*...*/ };

どこ

template<template<typename> data>> struct is_Data
{ static const bool value = /* some magic here (not the question) */; };

ただし、これには2つのテンプレートパラメータがあるため、User<>1つのテンプレートパラメータのみで宣言されている前の宣言と衝突します。他にできることはありますか?

(ノート

template<typename T,
         typename A= typename std::enable_if<is_Data<T>::value>::type>
class User<T> { /*...*/ };

動作しません(デフォルトのテンプレート引数は部分的な特殊化では使用できません)、また動作しません

template<typename T> class User<Data<T>> { /*...*/ };

から派生した型を許可しないためData<>、どちらも許可しません

template<typename T>
class User<typename std::enable_if<is_Data<T>::value,T>::type>
{ /*...*/ };

テンプレートパラメータTは部分特殊化では使用されないため。)

4

3 に答える 3

27

の元の宣言をUser<>適合させることができる場合

template<typename, typename=std::true_type> class User;

次に、解決策を見つけることができます(を使用する代わりに、Luc Dantonのコメントに従ってくださいstd::enable_if

template<typename>
struct is_Data : std::false_type {};
template<typename T>
struct is_Data<Data<T>> : std::true_type {};

template<typename T>
class User<T, typename is_Data<T>::type >
{ /* ... */ };

ただし、の元の定義を変更する必要があるため、これは元の質問に答えませんUser。私はまだより良い答えを待っていますこれは、他の解決策が不可能であることを決定的に示すものである可能性があります。

于 2012-10-12T13:09:09.543 に答える
11

あなたはまだより良い答えを待っているとあなたが言ったので、これが私の見解です。完璧ではありませんが、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で機能します。
于 2015-06-22T22:46:26.643 に答える
6

単一の条件が真の場合にのみ実装する必要があるため、最も簡単な解決策は静的アサーションを使用することです。SFINAEを必要とせず、誤って使用すると明確なコンパイルエラーが発生し、の宣言をUser<>調整する必要はありません。

template<typename T> class User {
  static_assert(is_Data<T>::value, "T is not (a subclass of) Data<>");
  /** Implementation. **/
};

参照:SFINAEの代わりに使用する場合 static_assert。これstatic_assertはc++11構造ですが、c++11より前のコンパイラで利用できる回避策は次のようにたくさんあります。

#define STATIC_ASSERT(consdition,name) \
  typedef char[(condition)?1:-1] STATIC_ASSERT_ ## name

の宣言をuser<>変更でき、の値に応じて2つの実装が必要な場合はis_Data、SFINAEを使用しないソリューションもあります。

template<typename T, bool D=is_Data<T>::value> class User;

template<typename T> class User<T, true> {
  static_assert(is_Data<T>::value, "T is not (a subclass of) Data<>"); // Optional
  /* Data implementation */
};

template<typename T> class User<T, false> {
  static_assert(!is_Data<T>::value, "T is (a subclass of) Data<>"); // Optional
  /* Non-data implementation */
};

静的アサーションは、ユーザーが誤ってテンプレート引数を指定していないかどうかのみをチェックしますDD明示的に指定されていない場合、静的アサーションは省略できます。

于 2014-02-19T20:14:10.050 に答える