我正在做一些实验,看看除了复制省略,RVO,NRVO案件之外的复制时间。
所以我写了一些像这样的代码:
class X {
public:
X() { std::cout << "Default constructor" << std::endl; }
X(const X&) { std::cout << "Copy constructor" << std::endl; }
X(X&&) { std::cout << "Move constructor" << std::endl; }
X& operator=(const X) {
std::cout << "Assignment operator" << std::endl;
return *this;
}
X& operator=(X&&) {
std::cout << "Move assignment operator" << std::endl;
return *this;
}
~X() { std::cout << "Destructor" << std::endl; }
};
class Y {
private:
X x;
public:
const X& getX() const {
std::cout << "getX" << std::endl;
return x;
}
};
int main() {
Y y;
std::cout << "assign to ref" << std::endl;
const X& x1 = y.getX();
(void)x1;
std::cout << "assign to const" << std::endl;
const X x2 = y.getX();
return 0;
}
我收到以下内容作为输出:
Default constructor
assign to ref
getX
assign to const
getX
Copy constructor
Destructor
Destructor
使用gcc或clang编译-O3并尝试-std = c ++ {11,14,17}时都生成相同的输出。
让我感到惊讶的是,在使用y.getX()时我没想到要执行任何副本。到一个const变量。我经常使用它来简化我对以下代码中的变量及其成员的访问,但我没有在const引用上执行它,而是我只是使用const希望编译器将其视为重命名。
有谁知道为什么要复制该副本?我想到的唯一原因是它使代码线程安全。如果有多个线程使用对象y,那么我对const的赋值毕竟不是那个const。因为它只会引用对象y中的成员x。其他线程可能会更改。但我不确定这是不是真的意图。
答案 0 :(得分:0)
要查看RVO对编译器强制使用NRVO的影响,请在下面的以下修改程序中使用-fno-elide-constructors
编译器开关。通过常用选项:
Default constructor 1
assign to ref
getX (with id: 1)
x1 (id:1)
assign to const
getX (with id: 1)
Copy constructor 2
x2 (id:2)
make_X copy
Default constructor 3
make_X (with id: 3)
x3 (id:3)
make_X ref
Default constructor 4
make_X (with id: 4)
x4 (id:4)
Destructor 4
Destructor 3
Destructor 2
Destructor 1
但是使用NRVO你会得到:
Default constructor 1
assign to ref
getX (with id: 1)
x1 (id:1)
assign to const
getX (with id: 1)
Copy constructor 2
x2 (id:2)
additional 1
Default constructor 3
make_X (with id: 3)
Move constructor 4
Destructor 3
Move constructor 5
Destructor 4
x3 (id:5)
additional 2
Default constructor 6
make_X (with id: 6)
Move constructor 7
Destructor 6
x4 (id:7)
Destructor 7
Destructor 5
Destructor 2
Destructor 1
代码示例:
#include <iostream>
int global_id;
class X {
public:
X() : id(++global_id) {
std::cout << "Default constructor " << id << std::endl;
}
X(const X&) : id(++global_id) {
std::cout << "Copy constructor " << id << std::endl;
}
X(X&&) : id(++global_id) {
std::cout << "Move constructor " << id << std::endl;
}
X& operator=(const X&) {
std::cout << "Assignment operator " << id << std::endl;
return *this;
}
X& operator=(X&&) {
std::cout << "Move assignment operator " << id << std::endl;
return *this;
}
~X() {
std::cout << "Destructor " << id << std::endl;
}
int id;
};
class Y {
X x;
public:
const X& getX() const {
std::cout << "getX (with id: " << x.id << ')' << std::endl;
return x;
}
X make_X() const {
X extra;
std::cout << "make_X (with id: " << extra.id << ')' << std::endl;
return extra;
}
};
int main()
{
Y y;
std::cout << "assign to ref" << std::endl;
const X& x1 = y.getX();
std::cout << "x1 (id:" << x1.id << ")\n";
(void) x1;
std::cout << "assign to const" << std::endl;
const X x2 = y.getX();
std::cout << "x2 (id:" << x2.id << ")\n";
std::cout << "make_X copy" << std::endl;
const X x3 = y.make_X();
std::cout << "x3 (id:" << x3.id << ")\n";
std::cout << "make_X ref" << std::endl;
const X& x4 = y.make_X();
std::cout << "x4 (id:" << x4.id << ")\n";
return 0;
}
如你所见,RVO真的只能使用局部变量。