wai*_*r92 2 c++ copy-elision c++17
我有巨大的结构 DataFrom 和 Data (实际上有不同的成员)。数据是从 DataFrom 创建的。
struct DataFrom{
int a = 1;
int b = 2;
};
static DataFrom dataFrom;
struct Data{
int a;
int b;
};
class DataHandler{
public:
static Data getData(const DataFrom& data2){
Data data;
setA(data, data2);
setB(data, data2);
return data;
}
private:
static void setA(Data& dest, const DataFrom& source){
dest.a = source.a;
}
static void setB(Data& dest, const DataFrom& source){
dest.b = source.b;
}
};
int main(){
auto data = DataHandler2::getData(dataFrom); // copy of whole Data structure
// ...
return 0;
}
Run Code Online (Sandbox Code Playgroud)
由于Data很大,在getData函数中,有整个Data结构的复制。可以以优雅的方式以某种方式防止这种情况吗?
我有一个想法:
static void getData( Data& data, const DataFrom& data2);
Run Code Online (Sandbox Code Playgroud)
但我更愿意将数据作为返回值而不是输出参数来检索。
这里有两个潜在的“复制危险”需要解决:
getData()在 的第一行 main(),您评论了“整个数据结构的副本” - 正如评论者所指出的,由于命名返回值优化(简称 NRVO),实际上不会复制该结构。几年前,您可以在这篇不错的博客文章中阅读到它:
简而言之:编译器对其进行了安排,以便data在getData函数内部调用 from 时main(),实际上是datain main的别名。
data和data2第二个“复制恐慌”是用setA()和setB()。在这里,您必须更加积极主动,因为您在同一个函数中确实有两个实时有效的结构体 -data并且data2在getData(). 事实上,如果Data和DataFrom只是大型结构 - 那么您将按照您编写代码的方式进行大量从data2to复制data。
将语义移到救援中
但是,如果您DataFrom持有对某些已分配存储的引用,例如,std::vector<int> a而不是int[10000] a- 您可以从您的位置移动DataFrom而不是从它复制 - 通过getData()使用签名static Data getData(DataFrom&& data2)。在此处阅读有关搬家的更多信息:
在我的例子,这将意味着你现在会用的原始缓冲区data2.a为您data-不复制该缓冲区其他地方的内容。但这将意味着您data2以后不能再使用,因为它的a领域已被蚕食、移出。
......或者只是“懒惰”。
除了基于移动的方法,您还可以尝试其他方法。假设你定义了这样的东西:
class Data {
protected:
DataFrom& source_;
public:
int& a() { return source_.a; }
int& b() { return source_.b; }
public:
Data(DataFrom& source) : source_(source) { }
Data(Data& other) : source_(other.source) { }
// copy constructor?
// assignment operators?
};
Run Code Online (Sandbox Code Playgroud)
NowData不是一个简单的结构体;它更像是 a DataFrom(以及其他一些字段和方法)的外观。这有点不太方便,但好处是您现在创建的 aData仅包含对 a 的引用,DataFrom而没有复制任何其他内容。在访问时,您可能需要取消引用指针。
其他注意事项:
你DataHandler被定义为一个类,但它看起来只是一个命名空间。您永远不会实例化“数据处理程序”。考虑阅读:
我的建议不涉及任何 C++17。移动语义是在 C++11 中引入的,如果你选择“懒惰”的方法 - 即使在 C++98 中也能工作。