(这是来自"“decltype(auto)”变量有任何实际用例吗? https://stackoverflow.com/questions/57438217/are-there-any-realistic-use-cases-for-decltypeauto-variables/57440814")
考虑以下场景 - 我想传递一个函数f
到另一个函数invoke_log_return
这将:
Invoke f
;
打印一些东西到stdout;
返回结果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;
}
}
让我们考虑一下各种可能性:
您可以看到一个测试应用程序在 godbolt.org 上 https://gcc.godbolt.org/z/iwhVHN。如你看到的,g++
执行 NRVO 的prvalue情况下,同时clang++
没有。
问题:
这是“完美”返回的最短方法吗?decltype(auto)
函数中的变量?有没有更简单的方法来实现我想要的?
Can the if constexpr { ... } else { ... }
模式被提取到一个单独的函数?提取它的唯一方法似乎是宏。
有什么充分的理由吗clang++
不执行 NRVOprvalue上面的情况?是否应该将其报告为潜在的增强功能,或者是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)();
}
While invoke_log_return_scope
更短,这需要函数行为的不同思维模型和新抽象的实现。令人惊讶的是,两者g++
and clang++
使用此解决方案执行 RVO/复制消除。
godbolt.org 上的实例 https://gcc.godbolt.org/z/7Zl9b_
正如所提到的,这种方法的一个主要缺点本·沃伊特 https://stackoverflow.com/users/103167/ben-voigt,是返回值f
不能是日志消息的一部分。