Vit*_*meo 24 c++ copy-elision auto c++17 decltype-auto
(这是“ “ decltype(auto)变量是否有实际用例? ”的后续文章)
考虑以下情况-我想将一个函数传递f给另一个函数invoke_log_return,该函数将:
调用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;
}
}
Run Code Online (Sandbox Code Playgroud)
让我们考虑各种可能性:
当f返回prvalue时:
result 将成为一个对象;
invoke_log_return(f)将是一个prvalue(有资格使用复制省略)。
当f返回左值或x值:
result 将作为参考;
invoke_log_return(f)将是一个左值或x 值。
您可以在godbolt.org上看到一个测试应用程序。如您所见,g++对prvalue情况执行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)();
}
Run Code Online (Sandbox Code Playgroud)
虽然invoke_log_return_scope要短得多,但这需要对功能行为和新抽象的实现使用不同的思维模型。出人意料的是,两者g++并clang++用此溶液进行RVO /复印,省音。
正如Ben Voigt所提到的,这种方法的一个主要缺点是的返回值f不能成为日志消息的一部分。
我们可以使用修改后的版本std::forward:(避免名称转发以防止 ADL 问题)
template <typename T>
T my_forward(std::remove_reference_t<T>& arg)
{
return std::forward<T>(arg);
}
Run Code Online (Sandbox Code Playgroud)
该函数模板用于转发decltype(auto)变量。它可以这样使用:
template <typename F>
decltype(auto) invoke_log_return(F&& f)
{
decltype(auto) result{std::forward<F>(f)()};
std::printf(" ...logging here...\n");
return my_forward<decltype(result)>(result);
}
Run Code Online (Sandbox Code Playgroud)
这样,如果std::forward<F>(f)()返回
纯右值,则为result非引用,并invoke_log_return返回非引用类型;
一个左值,那么result是一个左值引用,并invoke_log_return返回一个左值引用类型;
xvalue,则为result右值引用,并invoke_log_return返回右值引用类型。
(基本上是从我的/sf/answers/4020857011/复制的)