我一直在通過SFINAE和Curiously Recurring模板模式成語實現總排序,現在已經有一段時間了。總的想法如下:奇怪的循環模式和Sfinae
- 定義用於檢查關係運算符模板(
<
,>
等) - 定義限定總體排序操作符基類。
- 定義運算符檢測的基類。
- 從基類繼承。
爲簡單起見,本例中我忽略了==
和!=
運算符。
關係運算符檢測
我第一次定義類靜態檢查類是否定義了特定的功能。例如,在此我檢測到存在小於運算符或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
繼承
所有這些措施,單獨工作。但是,當我使用奇怪的循環模式實際繼承基類時,所得到的類僅實現這些運算符中的一個,並且檢測算法失敗。
例
我的問題歸結爲一個最小的例子包括核心部分組成:操作者檢測(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,理想情況下,它會編譯。我會編輯我的問題。 –