2015-11-12 1 views
0

Я думал, что это должно быть легко, но я боролся с ним какое-то время, поэтому я подумал, что я должен спросить здесь.Как использовать SFINAE для создания полиполнения для отсутствующих значений в классе enum?

Я хочу сделать шаблон metafunction, который принимает тип, соответствующий C++ 11 класса в перечислениях в качестве аргумента и возвращает INT:

  • Если класс перечисления E имеет значение перечислителя a, затем верните static_cast<int>(E::a).
  • Если класс перечисления E не имеет значения перечислителя a, тогда возвращается 42.

Тогда я хотел бы сделать шаблонную функцию, которая принимает во время выполнения экземпляра некоторого перечислимого класса E, статические отбрасывает его в целое, и проверяет, если он соответствует этому metafunction.

Я пробовал много итераций, шаблонов структуры и использования шаблона частичной специализации, чтобы попытаться отличить, если E::a существует, также используя шаблоны функций ... Я не уверен, могу ли я восстановить все, что я пробовал, но вот самое недавняя итерация:

template <typename E> 
inline int get_a_int_val(int result = E::a) { return result; } 

template <typename E> 
inline int get_a_int_val(int result = 42) { return result; } 

template <typename E> 
inline bool is_a_val(const E & input) { 
    return static_cast<int>(input) == get_a_int_val<E>(); 
} 

Это не работает, потому что я переопределяю аргументы по умолчанию.

template <typename E, int result = E::a> 
inline int get_a_int_val() { return result; } 

template <typename E, int result = 42> 
inline int get_a_int_val() { return result; } 

template <typename E> 
inline bool is_a_val(const E & input) { 
    return static_cast<int>(input) == get_a_int_val<E>(); 
} 

Это не работает, поскольку параметры непигового типа не могут зависеть от параметров типа.

template <typename E> 
struct get_a_int_val { 
    static const int value = 42; 
}; 

template <typename E> 
struct get_a_int_val<E> { 
    static const int value = static_cast<int>(E::a); 
}; 

template <typename E> 
inline bool is_a_val(const E & input) { 
    return static_cast<int>(input) == get_a_int_val<E>::value; 
} 

Это не работает, потому что

error: 
    class template partial specialization does not specialize any template 
    argument; to define the primary template, remove the template argument 
    list 

Что такое правильный способ сделать это?


Мотивация:

Причина, по которой я хочу сделать это, я хочу работать вокруг того, что, кажется, ошибка в libstdC++, который я сообщил здесь: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68307

В C++ 11 в заголовке <system_error>, существует пучка значений перечислителя для std::errc, которые должны быть определены, однако, при воспроизведении некоторых из них не хватает. Это вызывает ошибки компиляции в моей программе, потому что, в зависимости от того, как настроено Asio, lib::asio::errc может быть typedef'd до std::errc, а websocketpp предполагает, что lib::asio::errc::operation_canceled является определенным символом. Я хотел скомпоновать какую-то прокладку, которую я могу использовать в коде websocketpp, чтобы он был определен приемлемо на любой платформе (либо до lib::asio::errc::operation_canceled, если он существует, либо в ECANCELED от <cerrno>, если нет.)

ответ

2

Вы можете сделать это во многих отношениях, один из них представляет собой следующее:

template <typename E, typename Enable = void> 
struct get_a_int_val { 
    static const int value = 42; 
}; 

template <typename E> 
struct get_a_int_val<E, typename std::enable_if<std::is_same<decltype(E::a), 
               decltype(E::a)>::value, void>::type>{ 
    static const int value = static_cast<int>(E::a); 
}; 

LIVE DEMO

1

Вы можете создать признаки для этого:

template <typename E> 
std::false_type has_a_impl(...); 

template <typename E> auto has_a_impl(int) -> decltype(E::a, std::true_type{}); 

template <typename E> 
using has_a = decltype(has_a_impl<E>(0)); 

И затем использовать его в SFINAE:

template <typename E> 
std::enable_if_t<has_a<E>::value, int> 
get_a_int_val() { return static_cast<int>(E::a); } 

template <typename E> 
std::enable_if_t<!has_a<E>::value, int> 
get_a_int_val() { return 42; } 

Demo

Смежные вопросы