目前正在阅读 cpr 请求库的代码库: https://github.com/whoshuu/cpr/blob/master/include/cpr/api.h
注意到这个库的接口(interface)经常使用完美转发。只是学习右值引用,所以这对我来说都是相对较新的。
根据我的理解,右值引用、模板化和转发的好处是被环绕的函数调用将通过右值引用而不是值来获取其参数。这避免了不必要的复制。它还可以防止由于引用推导而不得不生成一堆重载。
然而,根据我的理解,const 左值引用本质上做同样的事情。它避免了重载的需要,并通过引用传递所有内容。需要注意的是,如果被环绕的函数采用非常量引用,它将无法编译。
但是,如果调用堆栈中的所有内容都不需要非常量引用,那么为什么不通过 const 左值引用传递所有内容呢?
我想我的主要问题是,什么时候应该使用一个而不是另一个以获得最佳性能?尝试使用以下代码对此进行测试。得到了以下比较一致的结果:
编译器:gcc 6.3 操作系统:Debian GNU/Linux 9
<<<<
Passing rvalue!
const l value: 2912214
rvalue forwarding: 2082953
Passing lvalue!
const l value: 1219173
rvalue forwarding: 1585913
>>>>
这些结果在运行之间保持相当一致。似乎对于右值 arg,const l 值签名稍微慢一些,尽管我不确定为什么,除非我误解了这一点并且 const 左值引用实际上复制了右值。
对于左值arg,我们看到计数器,右值转发较慢。为什么会这样?引用推导不应该总是产生对左值的引用吗?如果是这样的话,就性能而言,它不应该或多或少等同于 const 左值引用吗?
#include <iostream>
#include <string>
#include <utility>
#include <time.h>
std::string func1(const std::string& arg) {
std::string test(arg);
return test;
}
template <typename T>
std::string func2(T&& arg) {
std::string test(std::forward<T>(arg));
return test;
}
void wrap1(const std::string& arg) {
func1(arg);
}
template <typename T>
void wrap2(T&& arg) {
func2(std::forward<T>(arg));
}
int main()
{
auto n = 100000000;
/// Passing rvalue
std::cout << "Passing rvalue!" << std::endl;
// Test const l value
auto t = clock();
for (int i = 0; i < n; ++i)
wrap1("test");
std::cout << "const l value: " << clock() - t << std::endl;
// Test rvalue forwarding
t = clock();
for (int i = 0; i < n; ++i)
wrap2("test");
std::cout << "rvalue forwarding: " << clock() - t << std::endl;
std::cout << "Passing lvalue!" << std::endl;
/// Passing lvalue
std::string arg = "test";
// Test const l value
t = clock();
for (int i = 0; i < n; ++i)
wrap1(arg);
std::cout << "const l value: " << clock() - t << std::endl;
// Test rvalue forwarding
t = clock();
for (int i = 0; i < n; ++i)
wrap2(arg);
std::cout << "rvalue forwarding: " << clock() - t << std::endl;
}
最佳答案
首先,here are与您的代码的结果略有不同。如评论中所述,编译器及其设置非常重要。特别是,您可能会注意到所有情况都有相似的运行时间,除了第一个,它的速度大约是它的两倍。
Passing rvalue!
const l value: 1357465
rvalue forwarding: 669589
Passing lvalue!
const l value: 744105
rvalue forwarding: 713189
让我们看看每种情况下到底发生了什么。
1) 调用wrap1("test")时,因为该函数的签名需要一个 const std::string & ,您传递的 char 数组将隐式转换为临时 std::string每次调用(即 n 次)的对象,这涉及值的拷贝*。然后将对该临时文件的 const 引用传递到 func1 中。 ,另一个std::string是从它构造的,它又涉及一个拷贝(因为它是一个 const 引用,它不能被移动,尽管它实际上是一个临时的)。尽管函数按值返回,但由于 RVO,如果使用返回值,则可以保证删除该拷贝。在这种情况下,不使用返回值,我不完全确定标准是否允许编译器优化 temp 的构造。 .我怀疑不会,因为通常这样的构造可能会产生明显的副作用(并且您的结果表明它没有得到优化)。综上所述,std::string 的全面 build 和破坏在这种情况下执行两次。
2) 调用wrap2("test")时, 参数类型为 const char[5] , 它作为右值引用一直转发到 func2 , 其中 std::string来自 const char[] 的构造函数被称为复制值。模板参数的推导类型 T是const char[5] &&而且,很明显,尽管它是一个右值引用,但它不能被移动(因为两者都是 const 而不是是 std::string )。与前一种情况相比,字符串的构造/销毁每次调用仅发生一次(const char[5] 文字始终在内存中,不会产生开销)。
3) 调用wrap1(arg)时,您将左值作为 const string & 传递通过链,并在 func1 中调用一个复制构造函数.
4) 调用wrap2(arg)时,这与之前的情况类似,因为 T 的推导类型是const std::string & .
5) 我假设您的测试旨在展示当需要在调用链底部制作参数拷贝时完美转发的优势(因此创建了 temp )。在这种情况下,您需要更换 "test"前两种情况下的参数 std::string("test")为了真正拥有std::string &&参数,并将您的完美转发修复为 std::forward<T>(arg) ,如评论中所述。在这种情况下,the results是:
Passing rvalue!
const l value: 1314630
rvalue forwarding: 595084
Passing lvalue!
const l value: 712461
rvalue forwarding: 720338
这与我们之前的类似,但现在实际上调用了一个移动构造函数。
我希望这有助于解释结果。可能还有一些与函数调用内联和其他编译器优化相关的其他问题,这将有助于解释案例 2-4 之间较小的差异。
关于使用哪种方法的问题,我建议阅读 Scott Meyer 的“Effective Modern C++”第 23-30 项。对于书籍引用而不是直接答案表示歉意,但没有 Elixir ,最佳选择总是视情况而定,因此最好只了解每个设计决策的权衡。
* 由于短字符串优化,复制构造函数可能涉及也可能不涉及动态内存分配;感谢 ytoledano 在评论中提出这个问题。此外,我在整个答案中都隐含地假设拷贝比移动要昂贵得多,但情况并非总是如此。
关于c++ - 何时更喜欢 const 左值引用而不是右值引用模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46168394/
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
我几天前在我的rubyonrails2.3.2上安装了Sphinx和Thinking-Sphinx,基本搜索效果很好。这意味着,没有任何条件。现在,我想用一些条件过滤搜索。我有公告模型,索引如下所示:define_indexdoindexestitle,:as=>:title,:sortable=>trueindexesdescription,:as=>:description,:sortable=>trueend也许我错了,但我注意到只有当我将:sortable=>true语法添加到这些属性时,我才能将它们用作搜索条件。否则它找不到任何东西。现在,我还在使用acts_as_tag
如果names为nil,则以下中断。我怎样才能让这个map只有在它不是nil时才执行?self.topics=names.split(",").mapdo|n|Topic.where(name:n.strip).first_or_create!end 最佳答案 其他几个选项:选项1(在其上执行map时检查split的结果):names_list=names.try(:split,",")self.topics=names_list.mapdo|n|Topic.where(name:n.strip).first_or_create!e
我正在尝试将一个资源属性的默认值设置为另一个属性的值。我正在为我正在构建的tomcat说明书定义一个资源,其中包含以下定义。我想要可以独立设置的“名称”和“服务名称”属性。当未设置服务名称时,我希望它默认为为“名称”提供的任何内容。以下不符合我的预期:attribute:name,:kind_of=>String,:required=>true,:name_attribute=>trueattribute:service_name,:kind_of=>String,:default=>:name注意第二行末尾的“:default=>:name”。当我在Recipe的新block中引用我
我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“