(This is a follow-up from "Are there any realistic use cases for `decltype(auto)` variables?")
Consider the following scenario - I want to pass a function f
to another function invoke_log_return
which will:
Invoke f
;
Print something to stdout;
Return the result of f
, avoiding unnecessary copies/moves and allowing copy elision.
Note that, if f
throws, nothing should be printed to stdout. This is what I have so far:
template <typename F> decltype(auto) invoke_log_return(F&& f) { decltype(auto) result{std::forward<F>(f)()}; std::printf(" ...logging here...\n"); if constexpr(std::is_reference_v<decltype(result)>) { return decltype(result)(result); } else { return result; } }
Let's consider the various possibilities:
When f
returns a prvalue:
result
will be an object;
invoke_log_return(f)
will be a prvalue (eligible for copy elision).
When f
returns an lvalue or xvalue:
result
will be a reference;
invoke_log_return(f)
will be a lvalue or xvalue.
You can see a test application here on godbolt.org. As you can see, g++
performs NRVO for the prvalue case, while clang++
doesn't.
Questions:
Is this the shortest possible way of "perfectly" returning a decltype(auto)
variable out of a function? Is there a simpler way to achieve what I want?
Can the if constexpr { ... } else { ... }
pattern be extracted to a separate function? The only way to extract it seems to be a macro.
Is there any good reason why clang++
does not perform NRVO for the prvalue case above? Should it be reported as a potential enhancement, or is g++
's NRVO optimization not legal here?
Here's an alternative using a on_scope_success
helper (as suggested by Barry Revzin):
template <typename F> struct on_scope_success : F { int _uncaught{std::uncaught_exceptions()}; on_scope_success(F&& f) : F{std::forward<F>(f)} { } ~on_scope_success() { if(_uncaught == std::uncaught_exceptions()) { (*this)(); } } }; template <typename F> decltype(auto) invoke_log_return_scope(F&& f) { on_scope_success _{[]{ std::printf(" ...logging here...\n"); }}; return std::forward<F>(f)(); }
While invoke_log_return_scope
is much shorter, this requires a different mental model of the function behavior and the implementation of a new abstraction. Surprisingly, both g++
and clang++
perform RVO/copy-elision with this solution.
live example on godbolt.org
One major drawback of this approach, as mentioned by Ben Voigt, is that the return value of f
cannot be part of the log message.
2) decltype Keyword: It inspects the declared type of an entity or the type of an expression. ‘auto’ lets you declare a variable with a particular type whereas decltype lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression.
Note: The variable declared with auto keyword should be initialized at the time of its declaration only or else there will be a compile-time error. Note: We have used typeid for getting the type of the variables. Typeid is an operator which is used where the dynamic type of an object needs to be known.
Decltype is auto's not-evil twin. Auto lets you declare a variable with a particular type; decltype lets you extract the type from a variable (or any other expression). What do I mean?
You can use decltype for pretty much any expression, including to express the type for a return value from a method. Hmm, that sounds like a familiar problem doesn't it? What if we could write:
That's the simplest and most clear way to write it:
template <typename F> auto invoke_log_return(F&& f) { auto result = f(); std::printf(" ...logging here... %s\n", result.foo()); return result; }
The GCC gets the right (no needless copies or moves) expected result:
s() in main prvalue s() ...logging here... Foo! lvalue s(const s&) ...logging here... Foo! xvalue s(s&&) ...logging here... Foo!
So if code is clear, have ever the same functionality but is't optimized to run as much as the competitors does it's a compiler optimization failure and clang should work it out. That's the kind of problem that make lot more sense solved in the tool instead the application layer implementation.
https://gcc.godbolt.org/z/50u-hT
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With