Использование SFINAE для проверки того, завершен ли тип или нет

Можно ли проверить с помощью SFINAE, если тип полностью определен?

Например

template <class T> struct hash;
template <>        struct hash<int> {};

// is_defined_hash_type definition...

enum Enum { A, B, C, D };

static_assert (  is_defined_hash_type<int> ::value, "hash<int> should be defined");
static_assert (! is_defined_hash_type<Enum>::value, "hash<Enum> should not be defined");

Решение не должно изменять хэш-структуру.

Ответы

Ответ 1

Вы можете сделать is_complete типа is_complete, используя тот факт, что он плохо сформирован для оценки sizeof(T) для неполного типа T:

template <typename T>
struct is_complete_helper {
    template <typename U>
    static auto test(U*)  -> std::integral_constant<bool, sizeof(U) == sizeof(U)>;
    static auto test(...) -> std::false_type;
    using type = decltype(test((T*)0));
};

template <typename T>
struct is_complete : is_complete_helper<T>::type {};

и использовать его для проверки is_defined_hash_type<T> путем определения того, завершено ли hash<T>. (Live at Coliru)

Как говорит Даниил в своем ответе, полезность такой вещи ограничена. Этот признак фактически не проверяет, завершен ли тип в точке кода, где вы запрашиваете, он проверяет, был ли тип завершен в точке в программе, где этот признак был впервые создан для заданного типа.

Ответ 2

Это невозможно. Причина в том, что вам нужно будет определить is_defined_hash_type<T> но может быть только одно определение. Но если вы позже определите T, определение is_defined_hash_type<T> даст другой результат, следовательно, другое определение, и это не разрешено. Это нарушение ODR (одно правило определения).

Ответ 3

Лучшее, что я придумал до сих пор, - это следующее, которое требует по крайней мере typedef с общим именем во всей специализации hash:

template <class T> struct hash;
template <>        struct hash<int> {
    typedef int value_type;
    };

template<class T>
constexpr bool is_defined_hash_type(typename hash<T>::value_type) {
  return true;
}

template<class T>
constexpr bool is_defined_hash_type(T) {
  return false;
}

int main()
{
  static_assert (  is_defined_hash_type< int >(0), "hash<int> should be defined");
  static_assert (! is_defined_hash_type< double>(0), "hash<Enum> should not be defined");
  return 0;
}

Синтаксис довольно уродливый из-за добавленного параметра (необходимого для запуска SFINAE). Если вы думаете, что это может быть дорога, я постараюсь почистить ее дальше.

Отказ от ответственности: я отнюдь не эксперт С++ 11, поэтому я, возможно, пропустил некоторые моменты, используя новые функции. В этом случае огонь по желанию, и я постараюсь исправить ответ.