Search code examples
c++gcccompiler-errorsg++decorator

G++ cannot deduce result type of a generic function


I am trying to write a method decorator timing that I intend to use as follows:

auto res = timing(my_func, arg1, arg2);

where res contains the result of my_func(arg1, arg2).

My implementation of timing looks like this:

template <typename FunctionT, typename ...ArgT, typename ResultT>
ResultT timing(FunctionT fn, ArgT... args) {
    // do something here
    ResultT result = fn(args...);
    // do something else here
    return result;
}

However, this doesn't compile because G++ cannot deduct ResultT of the above call, for example the following minimal NOT working example:

#include <iostream>

// put timing method here

float my_func(float f, float g) {
    return f + g;
}

int main() {
    auto res = timing(my_func, 0.f, 3.f);
    std::cout << "Result is: " << res;
}

throws the following compiler error when compiled with g++ test.cpp:

test.cpp: In function ‘int main()’:
test.cpp:19:22: error: no matching function for call to ‘timing(float (&)(float, float), float, float)’
   19 |     auto res = timing(my_func, 0.f, 3.f);
      |                ~~~~~~^~~~~~~~~~~~~~~~~~~
test.cpp:5:9: note: candidate: ‘template<class FunctionT, class ... ArgT, class ResultT> ResultT timing(FunctionT, ArgT ...)’
    5 | ResultT timing(FunctionT fn, ArgT... args) {
      |         ^~~~~~
test.cpp:5:9: note:   template argument deduction/substitution failed:
test.cpp:19:22: note:   couldn’t deduce template parameter ‘ResultT’
   19 |     auto res = timing(my_func, 0.f, 3.f);
      |                ~~~~~~^~~~~~~~~~~~~~~~~~~

Why cannot g++ deduce the template parameter ResultT, shouldn't it be given by the function my_func?

Besides, when I specifically provide the value of ResultT, it works:

template <typename ResultT, typename FunctionT, typename ...ArgT>
ResultT timing(FunctionT fn, ArgT... args) {
    // as before
}

int main() {
    auto res = timing<float>(my_func, 0.f, 3.f);
}

Is there a way to compile this without explicitly mentioning the return value of my function? In fact, the function(s) I intend to use this method with do have pretty lengthy type definitions.


Solution

  • Sure, just use auto as your return type:

    template <typename F, typename ...Args>
    auto timing(F&& f, Args&&... args) {
        auto result = std::forward<F>(f)(std::forward<Args>(args)...);
        return result;
    }
    

    If for some reason you feel truly compelled to name the type, you could also do this:

    template <typename F, typename ...Args>
    std::invoke_result_t<F, Args...> timing(F&& f, Args&&... args) {
        std::invoke_result_t<F, Args...> result = std::forward<F>(f)(std::forward<Args>(args)...);
        return result;
    }