有时声称即使只编译C 98代码,C 11/14也可以提升性能 . 理由通常是移动语义,因为在某些情况下,rvalue构造函数是自动生成的,或者现在是STL的一部分 . 现在我想知道这些案例以前是否已经由RVO或类似的编译器优化处理过了 .
我的问题是,如果你能给我一个C 98代码的实际例子,使用支持新语言功能的编译器,无需修改,运行得更快 . 我确实理解标准符合编译器不需要执行复制省略,因此移动语义可能会带来速度,但我希望看到一个较少病态的案例,如果你愿意的话 .
编辑:为了清楚,我不是在问新编译器是否比旧编译器更快,而是如果有代码将-std = c 14添加到我的编译器标志中它会运行得更快(避免复制,但如果你能来除了移动语义之外的任何其他东西,我也会感兴趣)
2 回答
我知道5个一般类别,其中重新编译C 03编译器作为C 11可以导致无限的性能增加,这实际上与实现质量无关 . 这些都是移动语义的变体 .
std :: vector reallocate
每次在C 03中重新分配
foo
的缓冲区时,它都会在bar
中复制vector
.在C 11中它反而移动
bar::data
,这基本上是免费的 .在这种情况下,这依赖于
std
容器vector
内的优化 . 在下面的每种情况下,使用std
容器只是因为它们是在升级编译器时在C 11 _1523386中具有高效move
语义的C对象 . 不阻止它包含std
容器的对象也会继承自动改进的move
构造函数 .NRVO失败
当NRVO(命名返回值优化)失败时,在C 03中它回落到副本上,在C 11上它回落到移动上 . NRVO的失败很简单:
甚至:
我们有三个值 - 返回值,以及函数中的两个不同值 . Elision允许函数中的值与返回值“合并”,但不能彼此合并 . 它们都不能与返回值合并而不相互合并 .
基本问题是NRVO elision是脆弱的,并且不在
return
站点附近进行更改的代码可以突然在该位置大幅降低性能而不会发出诊断信息 . 在大多数NRVO失败案例中,C 11以move
结束,而C 03最终以副本结束 .返回函数参数
Elision在这里也是不可能的:
在C 11中这很便宜:在C 03中没有办法避免复制 . 函数的参数不能用返回值来省略,因为参数和返回值的生命周期和位置由调用代码管理 .
但是,C 11可以从一个移动到另一个 . (在较少玩具的例子中,可能会对
set
做些什么) .push_back或插入
最后,不会发生对容器的省略:但是C 11会重载rvalue移动插入操作符,从而节省了副本 .
在C 03中创建临时
whatever
,然后将其复制到向量v
中 . 分配2个std::string
缓冲区,每个缓冲区具有相同的数据,并丢弃一个缓冲区 .在C 11中,创建了临时
whatever
.whatever&&
push_back
重载然后move
s暂时进入向量v
. 分配一个std::string
缓冲区,并将其移动到向量中 . 空std::string
被丢弃 .作业
从@ Jarod42的答案中偷来了 .
分配时不能进行Elision,但移动可以 .
这里
some_function
返回一个候选者来自,但因为它不是用来直接构造一个对象,所以不能省略它 . 在C 03中,上述结果将临时内容复制到some_value
中 . 在C 11中,它被移动到some_value
,基本上是免费的 .为了达到上述目的,您需要一个能够为您合成移动构造函数和赋值的编译器 .
MSVC 2013在
std
容器中实现移动构造函数,但不在您的类型上合成移动构造函数 .所以包含
std::vector
和类似的类型不会得到这样的MSVC2013的改进,但将开始在MSVC2015中获得它们 .clang和gcc早就实现了隐式移动构造函数 . 如果你传递
-Qoption,cpp,--gen_move_operations
,那么英特尔的2013编译器将支持隐式生成移动构造函数(默认情况下它们并不是为了与MSVC2013交叉兼容) .如果你有类似的东西:
你在C 03中得到了一个副本,而你在C 11中得到了一个移动任务 . 所以你可以在这种情况下进行自由优化 .