C ++ 11 версия:
#include <type_traits>
template<class T>
struct satisfies_key_req{
struct nat{};
template<class K> static auto test(K* k) -> decltype(*k < *k);
template<class K> static nat test(...);
static bool const value = !std::is_same<decltype(test<T>(0)), nat>::value;
};
#include <iostream>
struct foo{};
int main(){
static bool const b = satisfies_key_req<int>::value;
std::cout << b << '\n';
static bool const b2 = satisfies_key_req<foo>::value;
std::cout << b2 << '\n';
}
Выход:
1
0
Ключевой момент, который я здесь использовал, это выражение SFINAE : auto test(K* k) -> decltype(*k < *k)
. Если выражение в типе конечного возврата недопустимо, то эта конкретная перегрузка test
удаляется из набора перегрузок. Другими словами, это СФИНАЕ.
§14.8.2 [temp.deduct]
p6 В определенных точках процесса вывода аргументов шаблона необходимо выбрать тип функции, который использует параметры шаблона, и заменить эти параметры шаблона соответствующими аргументами шаблона. Это делается в начале вывода аргумента шаблона, когда любые явно заданные аргументы шаблона подставляются в тип функции, и снова в конце вывода аргумента шаблона, когда любые аргументы шаблона, которые были выведены или получены из аргументов по умолчанию, подставляются .
p7 Подстановка происходит во всех типах и выражениях , которые используются в типе функции и в объявлениях параметров шаблона. Выражения включают в себя не только константные выражения , такие как те, которые появляются в границах массива или в качестве нетиповых аргументов шаблона , но также и общие выражения (т. Е. Неконстантные выражения) внутри sizeof
, decltype
и другие контексты, допускающие неконстантные выражения.
p8 Если подстановка приводит к неверному типу или выражению, вывод типа завершается неудачно. Недопустимый тип или выражение - это то, что было бы неправильно сформировано, если было бы написано с использованием замещенных аргументов. [...]
Вы можете использовать его в трех вариантах для класса Foo
, чтобы вызвать ошибку.
// static_assert, arguably the best choice
template< typename K >
class Foo
{
static_assert<satisfies_key_req<K>::value, "K does not satisfy key requirements");
// lots of other code here...
private:
std::map< K, size_t > m_map;
};
// new-style SFINAE'd, maybe not really clear
template<
typename K,
typename = typename std::enable_if<
satisfies_key_req<K>::value
>::type
>
class Foo
{
// lots of other code here...
private:
std::map< K, size_t > m_map;
};
// partial specialization, clarity similar to SFINAE approach
template<
typename K,
bool = satisfies_key_req<K>::value
>
class Foo
{
// lots of other code here...
private:
std::map< K, size_t > m_map;
};
template<typename K>
class Foo<K, false>;