3

In template meta programming, one can use SFINAE on the return type to choose a certain template member function, i.e.

 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]; }
};

However, this won't work if the function in question (_sum() in above example) has auto-detected return type, such as _func() in this example

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])); }
};

What else can be done to get SFINAE here?

Walter
  • 44,150
  • 20
  • 113
  • 196
  • I don't have a compiler to test, but does substituting the trailing return type with `-> std::enable_if::type` not work? (maybe drop an extra `typename` before the `std::enable_if`) – David Rodríguez - dribeas Jan 29 '13 at 17:58
  • This may also just be a compiler limitation. I think I recall that support for `auto` deducing the type of a function call itself, such as `auto f() -> decltype(f()) { /* ... */ }`, is still patchy. – Kerrek SB Jan 29 '13 at 18:01
  • Also note that the type of the function might be incorrect. You are returning the evaluation of `BinaryOp` with two arguments, but your trailing return type claims that the type is the return type of the evaluation of the `UnaryFunc`... – David Rodríguez - dribeas Jan 29 '13 at 18:05
  • Your template above does not work even without `auto` for me: am I doing something wrong? I just do `A<3> a; a.sum()`, and it refuses to compile. – Andy Prowl Jan 29 '13 at 18:09
  • @DavidRodríguez-dribeas yes, that worked. post the answer! – Walter Jan 29 '13 at 18:10
  • @AndyProwl sorry, there was a small bug. removed now. – Walter Jan 29 '13 at 18:11
  • @Walter: You can add the answer with what specifically worked and accept it – David Rodríguez - dribeas Jan 29 '13 at 18:13

1 Answers1

1

Following David Rodríguez - dribeas, the following code worked as intended:

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]); }
};

strictly speaking, we must also ensure that the binary operator op returns the correct type. For simplicity and brevity of the answer, I leave that for the reader to figure out ...

Walter
  • 44,150
  • 20
  • 113
  • 196