#c #c 11 #templates #template-specialization #sfinae
#c #c 11 #шаблоны #шаблон-специализация #sfinae
Вопрос:
Я пытаюсь создать функцию X, которая специализируется, когда предоставляется функция-член Y, и если функция-член Y не предоставлена, функция X использует глобальную функцию 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(Containeramp;amp; 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(Containeramp;amp; container) {
return y(std::forward<Container>(container);
}
Но в случае, когда контейнер имеет как функцию-член y, так и глобальную функцию, не являющуюся членом, y также работает над ним. Ошибка компиляции, поскольку для второй версии функции аргумент шаблона неверно сформирован, поскольку требуется функция-член y.
Есть идеи, как я могу решить эту проблему?
ПРИМЕЧАНИЕ: Это отличается от простого определения того, имеет ли класс функцию с заданной сигнатурой. Извините, если вопрос, кажется, задает это! Принятый ответ ниже должен прояснить мое намерение.
Комментарии:
1. @bolov Я отредактировал свой вопрос и добавил примечание. Приведенный ниже ответ Барри разъясняет то, что я хотел спросить!
Ответ №1:
Типичный подход заключается в отправке в пару функций, где одна является предпочтительной, если выполняется ваше условие, а другая — просто запасной вариант. Самое приятное, что вам даже не нужно enable_if
, только конечная decltype
:
template <class C>
auto do_something_impl(Camp;amp; c, int)
-> decltype(std::forward<C>(c).y())
{
return std::forward<C>(c).y();
}
template <class C>
auto do_something_impl(Camp;amp; c, ...)
-> decltype(y(std::forward<C>(c))
{
return y(std::forward<C>(c));
}
А теперь просто передайте 0
:
template <class C>
auto do_something(Camp;amp; c)
-> decltype(do_something_impl(std::forward<C>(c), 0))
{
return do_something_impl(std::forward<C>(c), 0);
}
Последовательность преобразования в int
лучше, чем в ...
, поэтому, если тип имеет нужную вам функцию-член, такая перегрузка будет предпочтительнее, даже если оба являются жизнеспособными кандидатами.
Комментарии:
1. Это прекрасно. Я никогда не думал об этом. У меня было ощущение, что конечные возвращаемые типы были бы полезны, но я не включил многоточия в уравнение. Я встал на недальновидный путь.
2. Просто из любопытства, как вы наткнулись на эту технику?
3. Ах, просто подумал об альтернативе этому. Иерархия наследования также может быть использована для имитации «предпочтительной» отправки с многоточиями здесь 🙂
4. @Любопытно, да, если вам нужно больше двух вариантов, есть другие вещи, которые вы можете сделать. С тремя вы можете сделать
int
,long
...
,choice<0>
но на данный момент я бы предпочелchoice<1>
,,, …5. @Barry Что, тебе не нравится
''
сchar
,int
long
user_defined_type_convertible_from_char
...
и,,,? Еретики! 😛