I'm experimenting with decltype(auto)
in a custom callable class template that simplifies the behavior of std::function
. My goal is to maintain the exact return type, including qualifiers like constness, reference etc, of the callable object when it is invoked. Here is the very simplified version of the class.
P.S. : C++ uses R
instead of decltype(auto)
:
https://en.cppreference.com/w/cpp/utility/functional/function/operator()
I was wondering if I can use decltype(auto)
with the same effect as R
template <typename>
class Callable;
template <typename R, typename... Args>
class Callable<R(Args...)>
{
public:
template<typename F>
Callable(F&& f) : func(std::forward<F>(f)) {}
decltype(auto) operator()(Args... args) {
return func(std::forward<Args>(args)...);
}
private:
std::decay_t<R(Args...)> func;
};
In your code, your type Callable
contains a pointer to function with return type R
. The deduction decltype(auto)
will yeild the same type as decltype(func(std::forward<Args>(args)...))
, which is always R
. Using R
or decltype(auto)
will yield the same type, but they are not 100% equivalent functionaly.
With decltype(auto)
, any instantiation of the function declaration will cause the definition to also be instatiated to deduce the return type. Here's an example:
auto f = Callable<int()>{[]{ return 1; }};
using result_t = decltype(f()); // Causes the body of operator() to be instantiated!
This can impact using C++20 concepts since trying to detect the presence of a member function might instantiate template that could end up in a hard error!
Using R
as return type will not instantiate the body until it is actually needed.