Mus*_*ful 17 c++ pass-by-value copy-elision
特定
struct Range{
Range(double from, double to) : from(from), to(to) {}
double from;
double to;
};
struct Box{
Box(Range x, Range y) : x(x), y(y) {}
Range x;
Range y;
};
Run Code Online (Sandbox Code Playgroud)
假设我们跑了Box box(Range(0.0,1.0),Range(0.0,2.0)).
启用优化的现代编译器是否可以避免Range在此构造期间完全复制对象?(即构建Range内部的对象box以开始?)
int*_*jay 24
实际上,在Range传递给构造函数的每个对象上执行了两个副本.第一种情况发生在将临时Range对象复制到函数参数中时.根据101010的回答中给出的参考,可以省略这一点.在某些特定情况下可以执行复制省略.
将函数参数复制到成员中时(如构造函数初始化列表中所指定),会发生第二个副本.这是不能省略的,这就是为什么你仍然看到YSC的答案中为每个参数制作了一个副本.
当复制构造函数具有副作用(例如YSC答案中的打印件)时,仍可以对第一个副本执行复制省略,但必须保留第二个副本.
但是,如果编译器不改变程序的观察行为,则编译器总是可以自由地进行更改(这称为"as-if"规则).这意味着如果复制构造函数没有副作用并且删除构造函数调用将不会更改结果,则编译器可以自由删除第二个副本.
您可以通过分析生成的程序集来看到这一点.在此示例中,编译器不仅优化副本,还优化Box对象本身的构造:
Box box(Range(a,b),Range(c,d));
std::cout << box.x.from;
Run Code Online (Sandbox Code Playgroud)
生成相同的程序集:
std::cout << a;
Run Code Online (Sandbox Code Playgroud)
它应该,但我没有让它工作(现场示例)。编译器可能会检测到构造函数的副作用并决定不使用复制省略。
#include <iostream>
struct Range{
Range(double from, double to) : from(from), to(to) { std::cout << "Range(double,double)" << std::endl; }
Range(const Range& other) : from(other.from), to(other.to) { std::cout << "Range(const Range&)" << std::endl; }
double from;
double to;
};
struct Box{
Box(Range x, Range y) : x(x), y(y) { std::cout << "Box(Range,Range)" << std::endl; }
Box(const Box& other) : x(other.x), y(other.y) { std::cout << "Box(const Box&)" << std::endl; }
Range x;
Range y;
};
int main(int argc, char** argv)
{
(void) argv;
const Box box(Range(argc, 1.0), Range(0.0, 2.0));
std::cout << box.x.from << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译运行:
clang++ -std=c++14 -O3 -Wall -Wextra -pedantic -Werror -pthread main.cpp && ./a.out
Run Code Online (Sandbox Code Playgroud)
输出:
Range(double,double)
Range(double,double)
Range(const Range&)
Range(const Range&)
Box(Range,Range)
1
Run Code Online (Sandbox Code Playgroud)
是的,可以,特别是这种复制省略上下文属于标准12.8/p31.3 复制和移动类对象 [class.copy]中指定的复制省略标准:
(31.3) -- 当尚未绑定到引用 (12.2) 的临时类对象被复制/移动到具有相同类型的类对象时(忽略 cv 限定),可以通过以下方式省略复制/移动操作将临时对象直接构造到省略的复制/移动的目标中。
任何下降编译器都在这个特定的上下文中应用复制省略。然而,在OP示例中,发生了两个副本。
Box(无法删除)。您可以在这个演示中看到它,其中复制构造函数仅被调用两次。
还要记住,因为标准允许在特定上下文中进行复制省略优化,但这并不意味着编译器供应商有义务这样做。复制省略是唯一允许的可以改变可观察到的副作用的优化形式。因此,由于某些编译器不会在允许的每种情况下(例如,在调试模式下)执行复制省略,因此依赖复制/移动构造函数和析构函数副作用的程序是不可移植的。
| 归档时间: |
|
| 查看次数: |
3167 次 |
| 最近记录: |