(这是对“`decltype(auto)` 变量有任何实际用例吗? ”的后续报道)
考虑以下场景 - 我想将一个函数传递f
给另一个函数invoke_log_return
,该函数将:
调用
f
;打印一些东西到标准输出;
返回 的结果
f
,避免不必要的复制/移动并允许复制省略。
请注意,如果f
抛出,则不应将任何内容打印到stdout。这是我到目前为止所拥有的:
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;
}
}
让我们考虑各种可能性:
当
f
返回纯右值时:result
将是一个对象;invoke_log_return(f)
将是一个prvalue(有资格进行复制省略)。
当
f
返回左值或xvalue时:result
将作为参考;invoke_log_return(f)
将是左值或xvalue。
您可以在 godbolt.org 上看到一个测试应用程序。如您所见,对纯右值情况g++
执行 NRVO ,而clang++
没有。
问题:
这是
decltype(auto)
从函数中“完美”返回变量的最短方法吗?有没有更简单的方法来实现我想要的?可以将
if constexpr { ... } else { ... }
模式提取到单独的函数中吗?提取它的唯一方法似乎是宏。有什么好的理由
clang++
不为上述prvalue案例执行NRVO?应该将其报告为潜在的增强功能,还是g++
NRVO 优化在这里不合法?
这是使用助手的替代方法on_scope_success
(如 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)();
}
虽然invoke_log_return_scope
要短得多,但这需要函数行为的不同心智模型和新抽象的实现。令人惊讶的是,两者都g++
使用clang++
此解决方案执行 RVO/复制省略。
正如Ben Voigt所提到的,这种方法的一个主要缺点是 的返回值f
不能是日志消息的一部分。