Search code examples
c++template-meta-programminginvoke-result

Get return type of template method in a template class


I have a following not compiling code:

#include <iostream>
#include <type_traits>

template <typename T, typename... Args>
struct foo {};

template <typename T>
struct bar {

  template <typename... Args>
  auto func(Args... args)
  {
    return foo(*this, args...);
  }
};

template <typename T, typename... Args>
using bar_func_return_type = std::invoke_result_t<&bar<T>::func, Args...>; // ERROR HERE

int main()
{
  if constexpr (std::is_same_v<bar_func_return_type<int, float, double>, foo<int, floar, double>)
  {
    std::cout << "OK";
  }
  else {
    std::cout << "FAIL";
  }

  return 0;
}

I am trying to get the result type of bar::func method. I get an error Template argument for template type parameter must be a type when specifying bar_func_return_type. How can I get the desired return type?


Solution

  • Looks like their are a lot of details missing from your code. I tried to fix them and get something that actually wants to compile.

    Key ingredient to get return type = template <typename T, typename... Args> using bar_func_return_type = decltype(std::declval<bar<T>>().func(std::declval<Args>()...));

    Full code :

    #include <iostream>
    #include <type_traits>
    
    template <typename T, typename... Args>
    struct foo 
    {
        // foo missed a constructor with correct types
        foo(const T&, Args&&...)
        {
        };
    };
    
    template <typename T>
    struct bar 
    {
        // Args&& is a bit more flexible in practice
        template <typename... Args>
        auto func(Args&&... args)
        {
            return foo<bar,Args...>(*this, std::forward<Args>(args)...);
        }
    };
    
    // std::declval makes default instances ot the types specified
    // this is usually how I deduce return values (could be invoke_result_t is actually a bit more easy to use.
    template <typename T, typename... Args>
    using bar_func_return_type = decltype(std::declval<bar<T>>().func(std::declval<Args>()...));
    
    int main()
    {
        using retval_t = bar_func_return_type<int, float, double>;
    
        // No need to do a runtime check, you can already
        // test at compile time using static_assert
        static_assert(std::is_same_v<retval_t, foo<bar<int>, float, double>>);
        return 0;
    }