как использовать SFINAE для включения функции-члена, возвращающей `auto`

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

 template<int N> struct A {
   int sum() const noexcept
   { return _sum<N-1>(); }
 private:
   int _data[N];
   template<int I> typename std::enable_if< I,int>::type _sum() const noexcept
   { return _sum<I-1>() + _data[I]; }
   template<int I> typename std::enable_if<!I,int>::type _sum() const noexcept
   { return _data[I]; }
};

Однако это не сработает, если рассматриваемая функция (_sum() в приведенном выше примере) имеет автоматически определяемый тип возвращаемого значения, например _func() в этом примере.

template<int N> class A
{
   /* ... */
private:
  // how to make SFINAE work for _func() ?
  template<int I, typename BinaryOp, typename UnaryFunc>
  auto _func(BinaryOp op, UnaryFunc f) const noexcept -> decltype(f(_data[0]))
  { return op(_func<I-1>(op,f),f(_data[I])); }
};

Что еще можно сделать, чтобы получить SFINAE здесь?


person Walter    schedule 29.01.2013    source источник
comment
У меня нет компилятора для тестирования, но не работает ли замена завершающего возвращаемого типа на -> std::enable_if<I, decltype(f(_data[0]))>::type? (может быть, добавите typename перед std::enable_if)   -  person David Rodríguez - dribeas    schedule 29.01.2013
comment
Это также может быть просто ограничением компилятора. Кажется, я припоминаю, что поддержка auto для вывода типа самого вызова функции, например auto f() -> decltype(f()) { /* ... */ }, все еще неоднородна.   -  person Kerrek SB    schedule 29.01.2013
comment
Также обратите внимание, что тип функции может быть неправильным. Вы возвращаете оценку BinaryOp с двумя аргументами, но ваш конечный возвращаемый тип утверждает, что этот тип является типом возвращаемого значения оценки UnaryFunc...   -  person David Rodríguez - dribeas    schedule 29.01.2013
comment
Ваш шаблон выше не работает даже без auto у меня: я что-то не так делаю? Я просто делаю A<3> a; a.sum(), и он отказывается компилироваться.   -  person Andy Prowl    schedule 29.01.2013
comment
@DavidRodríguez-dribeas да, это сработало. опубликуйте ответ!   -  person Walter    schedule 29.01.2013
comment
@AndyProwl извините, произошла небольшая ошибка. удален сейчас.   -  person Walter    schedule 29.01.2013
comment
@Walter: вы можете добавить ответ о том, что конкретно сработало, и принять его.   -  person David Rodríguez - dribeas    schedule 29.01.2013


Ответы (1)


Вслед за David Rodríguez - dribeas следующий код работал как положено:

template<int N> class A
{
  int min_abs() const noexcept
  {
    return _func<N-1>([](int x, int y)->int { return std::min(x,y); },
                      [](int x)->int { return std::abs(x); });
  }
private:
  int _data[N];

  template<int I, typename BinaryOp, typename UnaryFunc>
  auto _func(BinaryOp op, UnaryFunc f) const noexcept
    -> typename std::enable_if< I>,decltype(f(_data[0]))>::type
  { return op(_func<I-1>(op,f),f(_data[I])); }

  template<int I, typename BinaryOp, typename UnaryFunc>
  auto _func(BinaryOp op, UnaryFunc f) const noexcept
    -> typename std::enable_if<!I>,decltype(f(_data[0]))>::type
  { return f(_data[I]); }
};

Строго говоря, мы также должны убедиться, что бинарный оператор op возвращает правильный тип. Для простоты и краткости ответа я оставляю это на усмотрение читателя...

person Walter    schedule 29.01.2013