Вывод возвращаемого типа шаблонных функций-членов в CRTP

Можно ли вывести возвращаемый тип шаблонной функции-члена в базовый класс CRTP?

В то время как вывод типов аргументов хорошо работает, он терпит неудачу с возвращаемым типом. Рассмотрим пример ниже.

#include <iostream>

template <typename Derived>
struct base
{
  template <typename R, typename T>
  R f(T x)
  {
    return static_cast<Derived&>(*this).f_impl(x);
  }
};

struct derived : base<derived>
{
  bool f_impl(int x)
  {
    std::cout << "f(" << x << ")" << std::endl;
    return true;
  }
};

int main()
{
  bool b = derived{}.f(42);
  return b ? 0 : 1;
}

Это приводит к следующей ошибке:

  bool b = derived{}.f(42);
           ~~~~~~~~~~^
crtp.cc:7:5: note: candidate template ignored: couldn't infer template argument 'R'
  R f(T x)
    ^
1 error generated.

Мое интуитивное предположение заключается в том, что если компилятор способен вывести тип int для аргумента f, он также должен работать для return bool, поскольку оба типа известны во время создания шаблона.

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

РЕДАКТИРОВАТЬ 1

В случае, когда функция имеет один или несколько шаблонных аргументов, Dietmar Kühl предоставил решение, основанное на отсрочке создания экземпляра шаблона, используя слой косвенности. К сожалению, это не работает, когда функция базового класса не имеет никаких аргументов, например:

template <typename R>
R g()
{
  return static_cast<Derived&>(*this).g_impl();
}

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

EDIT 2

Как указано Johannes Schaub, С++ 11 содержит аргументы шаблона по умолчанию, поэтому всегда можно сделать g зависимым от произвольного типа, а затем применить решение Dietmar:

template <typename T = void>
auto g() -> typename g_impl_result<Derived, T>::type
{
  return static_cast<Derived&>(*this).g_impl();
}

РЕДАКТИРОВАТЬ 3

Эта проблема больше не существует в С++ 14, так как у нас есть возвращаемый тип вывода для нормальных функций, что позволяет нам просто написать:

template <typename Derived>
struct base
{
  template <typename T>
  auto f(T x)
  {
    return static_cast<Derived&>(*this).f_impl(x);
  }

  auto g()
  {
    return static_cast<Derived&>(*this).g_impl();
  }
};

struct derived : base<derived>
{
  bool f_impl(int x)
  {
    return true;
  }

  double g_impl()
  {
    return 4.2;
  }
};

Ответ 1

Дополнительное косвенное отношение - ваш друг:

template <typename D, typename T>
struct f_impl_result
{
    typedef decltype(static_cast<D*>(0)->f_impl(std::declval<T>())) type;
};

template <typename Derived>
struct base
{
    template <typename T>
    auto f(T x) -> typename f_impl_result<Derived, T>::type
    {
        return static_cast<Derived&>(*this).f_impl(x);
    }
};