What a clever use of the comma operator and operator overloading! Let’s start with these neat little classes.
First comes the log
class. It overloads the comma operator, accepts arguments of a specific type, turns them into log messages, and flushes them in its destructor.
So, on line 82
constexpr log_unified $log{};
You’ll see a global object named $log
; judging from the name, it’s meant to mimic some command-line logging syntax.
Next are two wrapper classes. return_wrapper
stores the incoming argument via move semantics in its constructor. Inside the overloaded comma operator it concatenates the new argument with the stored one, constructs a fresh return_wrapper, and returns it.
After that tiny tour we turn to testVoid4()
and its single line:
return $log, "testVoid4";
Here’s the fun part: testVoid4()
is declared void, yet it sports a return (something)
.
void testVoid4() {
return $log, "testVoid4";
}
This isn’t standard: the function’s fate is handed to whatever (something)
evaluates to. If (something)
were just a harmless expression, we’d be safe; but if it’s an object, we’ve effectively written return obj;
—and the compiler complains.
So what exactly is (something)
? Look closer: ($log, "testVoid4")
. Evaluated left-to-right, the first sub-expression is the global variable $log
. Therefore the comma becomes $log
’s overloaded operator,, i.e. $log.operator,( "testVoid4" )
, which expands to
return void_wrapper<std::decay_t<T>>(std::forward<T>(arg));
or concretely
void_wrapper<const char*>(std::forward<const char*>("testVoid4"));
—a temporary object! Alas, our little testVoid4()
ends up trying to return a tiny void_wrapper
, so the build fails.