2016-10-22 10 views
4

Я пытаюсь создать функцию X, которая специализируется, когда предоставляется функция-член Y, и если функция-член Y не предоставляется, функция X использует глобальную функцию-член-член Y для достижения такой же эффект.SFINAE с функциями и эмулированием частичной специализации шаблона

Я в настоящее время пытаюсь добиться этого с помощью следующего кода

template <typename Container, typename std::enable_if_t<std::is_same< 
      decltype(std::declval<Container>().y()), 
      decltype(std::declval<Container>().y())>::value>* = nullptr> 
void do_something(Container&& container) { 
    return std::forward<Container>().y(); 
} 

template <typename Container, typename std::enable_if_t<!std::is_same< 
      decltype(std::declval<Container>().y()), 
      decltype(std::declval<Container>().y())>::value>* = nullptr> 
void do_something(Container&& container) { 
    return y(std::forward<Container>(container); 
} 

Но в том случае, когда контейнер имеет как функцию-член у и глобальный не член функции у и работает на нем. Ошибка компиляции, потому что для второй версии функции аргумент шаблона плохо сформирован, потому что нужна функция-член y.

Любая идея, как я могу решить эту проблему?

ПРИМЕЧАНИЕ: Это отличается от простого определения того, имеет ли класс функцию данной сигнатуры. Извините, если вопрос, похоже, спрашивает об этом! Принятый ниже ответ должен сделать мое намерение более ясным.

+0

@bolov Я отредактировал мой вопрос и добавил примечание. Ответ Барри ниже поясняет, что я хотел спросить! – Curious

ответ

3

Типичный подход заключается в отправке на пару функций, где один предпочтительнее, если ваше условие выполнено, а другое - просто резерв. Хорошая вещь, что вы даже не нужно enable_if, только задняя decltype:

template <class C> 
auto do_something_impl(C&& c, int) 
    -> decltype(std::forward<C>(c).y()) 
{ 
    return std::forward<C>(c).y(); 
} 

template <class C> 
auto do_something_impl(C&& c, ...) 
    -> decltype(y(std::forward<C>(c)) 
{ 
    return y(std::forward<C>(c)); 
} 

А теперь просто проходят в 0:

template <class C> 
auto do_something(C&& c) 
    -> decltype(do_something_impl(std::forward<C>(c), 0)) 
{ 
    return do_something_impl(std::forward<C>(c), 0); 
} 

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

+0

Это красиво. Я никогда об этом не думал. У меня было ощущение, что возвратные типы возврата будут полезны, но я не включил эллипсы в уравнение. Я упал на близорукий маршрут. – Curious

+0

Просто из любопытства, как вы наткнулись на эту технику? – Curious

+0

Ах, просто подумал об альтернативе этому. Иерархия наследования также может использоваться для имитации «предпочтительной» отправки с эллипсами здесь :) – Curious