我正在编写高度通用的代码,我将函数调用的返回值作为const auto&
进行处理。例如:
const auto& value = foo();
目的是以尽可能最通用的方式编写它,以便它不对foo
的返回类型做出任何假设,它可以按值返回或通过引用返回而不会破坏客户端上的任何内容代码,即使该值是临时值。
但是这段代码的表现可能比:
const auto value = foo();
如果foo()
返回基本类型,例如int
或double
或enum
?
答案 0 :(得分:7)
我理解为什么你会认为它会产生运行时成本,但实际情况并非如此。
如果函数返回值类型,而不是将其作为引用,任何现代编译器都会立即将其折叠为值类型。编译后,您的const int&
将成为int
。您可以在此处对此进行测试:https://godbolt.org/g/vT2FdG。
答案 1 :(得分:2)
取决于。
在许多情况下,特别是当foo()
返回基本类型时,引用将被优化掉,两个版本之间没有区别。
但如果foo()
返回一个大型对象,则可能取决于NRVO是否适用。
构造auto value = foo();
启用NRVO,const auto& value = foo();
有时可以阻止它(demo)。所以非参考版本可以更快。
但是如果由于编写foo()
的方式而无法使用NRVO,则可以创建并复制临时对象。所以参考版本会更快。
注意:如果返回的对象不是可复制的,那么非引用版本将无法编译。
答案 2 :(得分:1)
基于comment,您可以使用C ++ 17 cookbook解决方案来处理接受转发参数的通用仿函数:
decltype(auto)
变量来捕获它。decltype(auto)
。std::invoke
简化呼叫网站。并真正支持各种好奇的仿函数类型。std::invoke_result
处理返回类型为void的极端情况。您不希望定义void类型的变量。一切都是为了这个模板(模板):
#include <type_traits>
#include <functional>
#include <utility>
template<class F, typename... Args>
decltype(auto) process_call(F&& f, Args&&... args) {
if constexpr (std::is_void_v<std::invoke_result_t<F, Args...>>) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
// Process without capturing
return;
}
else {
decltype(auto) ret = std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
// Use ret for your processing
return ret;
}
}
答案 3 :(得分:0)
您最好的选择是试用普通旧数据类型的结果,其中包括检查目标平台上生成的程序集。
对我来说,本能暗示
const int value = foo();
比
更快const int& value = foo();
因为设置引用的开销 - 包括终身扩展的可能性 - 可能大于普通旧数据类型的值副本。
如果截止值是平台依赖的,那么优化编译器可能会在任何情况下调整比赛场地。