私は今、SFINAEとCuriously Recurring Template Patternイディオムを使って総発注を実装しています。次のように一般的な考え方は次のとおりです。不思議な反復パターンと合成
- は、関係演算子をチェックしたテンプレート(
<
、>
など) - は全順序演算子を定義する基本クラスを定義定義します。
- オペレータ検出から基本クラスを定義します。
- 基本クラスから継承します。
簡略化のため、この例では==
と!=
演算子は無視しています。
関係演算子検出
私が最初に静的クラスは、特定の機能を定義するかどうかをチェックするためのクラスを定義します。たとえば、ここでは、より小さい演算子、またはoperator<
の存在を検出します。
template <typename T>
class has_less
{
protected:
template <typename C> static char &test(decltype(std::declval<C>() < std::declval<C>()));
template <typename C> static long &test(...);
public:
enum {
value = sizeof(test<T>(0)) == sizeof(char)
};
};
template <typename T>
constexpr bool has_less_v = has_less<T>::value;
私は少ないし、よりオペレータから全順序を定義するために、例えば、与えられたオペレータから全順序を実装するクラスを定義
を注文合計、私は次のように使用します。
template <typename T>
struct less_than_total
{
bool operator>(const T &t) { return t < static_cast<T&>(*this); }
bool operator>=(const T &t) { return !(static_cast<T&>(*this) < t); }
bool operator<=(const T &t) { return !(t < static_cast<T&>(*this)); }
};
基本クラス
私はその後、定義実装された演算子を検出することによって残りの演算子を実装するtypedefを作成する単一の基本クラス。
template <bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;
template <typename T>
using total_ordering = conditional_t< // has_less
has_less_v<T>,
less_than_total<T>,
conditional_t< // has_less_equal
has_less_equal_v<T>,
less_equal_total<T>,
conditional_t< // has_greater
has_greater_v<T>,
greater_total<T>,
conditional_t< // has_greater_equal
has_greater_equal_v<T>,
greater_equal_total<T>,
symmetric<T> // symmetry
> // has_greater_equal
> // has_greater
> // has_less_equal
>; // has_less
継承
これらの手順のすべて
、個別に、作品。しかし、奇妙な反復パターンを使用して基本クラスから実際に継承すると、結果のクラスはこれらの演算子のうちの1つだけを実装し、検出アルゴリズムは失敗します。例
Iは、コア部からなる最小の例に問題を煮詰めてきた:オペレータ検出(has_less
、has_greater
)、全順序の実装(total
)、簡略化された基本クラス(total
)を、およびこれらの関係演算子を使用する単純な構造体(A
)です。
#include <type_traits>
// DETECTION
template <typename T>
class has_less
{
protected:
template <typename C> static char &test(decltype(std::declval<C>() < std::declval<C>()));
template <typename C> static long &test(...);
public:
enum {
value = sizeof(test<T>(0)) == sizeof(char)
};
};
template <typename T>
class has_greater
{
protected:
template <typename C> static char &test(decltype(std::declval<C>() > std::declval<C>()));
template <typename C> static long &test(...);
public:
enum {
value = sizeof(test<T>(0)) == sizeof(char)
};
};
// TOTAL ORDERING
template <typename T>
struct less_than_total
{
bool operator>(const T &t) { return t < static_cast<T&>(*this); }
bool operator>=(const T &t) { return !(static_cast<T&>(*this) < t); }
bool operator<=(const T &t) { return !(t < static_cast<T&>(*this)); }
};
template <typename T>
struct symmetry
{};
template <bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;
template <typename T>
struct total: conditional_t<
has_less<T>::value,
less_than_total<T>,
symmetry<T>
>
{};
// TEST
struct A: total<A>
{
bool operator<(const A &r)
{
return true;
}
};
int main(void)
{
static_assert(has_less<A>::value, "");
static_assert(has_greater<A>::value, "");
return 0;
}
理想的には、この例では、コンパイルだろう、しかし、私が取得:
$ clang++ a.cpp -o a -std=c++14
a.cpp:79:5: error: static_assert failed ""
static_assert(has_less<A>::value, "");
^ ~~~~~~~~~~~~~~~~~~
a.cpp:80:5: error: static_assert failed ""
static_assert(has_greater<A>::value, "");
残念ながら、ベースクラスは継承時の演算子を検出していない、とSFINAEは、より少ないまたはそれ以上を検出しません結果のクラスの演算子。
質問やフォローアップ
私はこれが失敗した理由を、私は問題のない不思議な定期的なパターンで長時間メンバー関数の検出とメンバー型検出を行っているため、知っていただきたいと思い。私のコードに直接問題がないと仮定すると、そのような方法でトータルオーダーを実装するための回避策はありますか?
編集
私はstd::enable_if
を使って何をしたいのサブセットを達成することができますよ。この場合、唯一の簡単な答えは、すべてをoperator<
の形で実装してから、その演算子からの合計発注を実装することです。
template <typename T>
struct total
{
template <typename U = T>
typename std::enable_if<has_less<U>::value, bool>::type
bool operator>(const T &l, const T &r) { return r < t; }
};
の場合は、まだSFINAEを経由して私のオペレータの検出は、相続時に失敗しますが、継承されたメソッドのために成功した理由のための答えをしたいと思います。
この例ではどのようなことが起こりそうですか? – mascoj
@mascoj、理想的にはコンパイルします。私は私の質問を編集します。 –