문제

Is it possible to infer the return type of a templated member function in a CRTP base class?

While inferring argument types works well, it fails with the return type. Consider the example below.

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

This produces the following error:

  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.

My intuitive assumption is that if the compiler is capable of inferring type int for the argument to f, it should also work for the return bool, because both types are known at template instantiation time.

I tried using the trailing return type function syntax, but then failed to find a working expression to put in decltype.

EDIT 1

For the case where the function has one or more templated arguments, Dietmar Kühl provided a solution based on delaying template instantiation using on layer of indirection. Unfotunately, this does not work when a base class function does not have any argument, like this:

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

Attempts to use the same technique fail because there exist no dependent types. How does one handle this case?

EDIT 2

As pointed out by Johannes Schaub, C++11 features default template arguments, so it is always possible to make g dependent on an arbitrary type and then apply Dietmar's solution:

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

EDIT 3

This problem does not exist in C++14 anymore, since we have return type deduction for normal functions, allowing us to write simply:

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;
  }
};
도움이 되었습니까?

해결책

An extra indirection is your friend:

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);
    }
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top