Sometimes we may defer perfect returning like this:
template<typename Func, typename... Args> decltype(auto) call(Func f, Args&&... args) { decltype(auto) ret{f(std::forward<Args>(args)...)}; // ... return static_cast<decltype(ret)>(ret); } But in Jousttis's new book C++ Move Semantics - The Complete Guide, he says that code below is better:
template<typename Func, typename... Args> decltype(auto) call(Func f, Args&&... args) { decltype(auto) ret{f(std::forward<Args>(args)...)}; // ... if constexpr (std::is_rvalue_reference_v<decltype(ret)>) { return std::move(ret); // move xvalue returned by f() to the caller } else { return ret; // return the plain value or the lvalue reference } } Because the first piece of code "might disable move semantics and copy elision. For plain values, it is like having an unnecessary std::move() in the return statement." What's the difference between these two patterns? From my point of view, for plain values, decltype will deduce just the type itself, so it's just a static_cast<Type>(ret)(i.e. no operation at all) and the returned type is same as the declared type so that copy elision is possible. Is there anything that I take wrong?