gpt4 book ai didi

c++ - 传递值导致额外 move

转载 作者:可可西里 更新时间:2023-11-01 15:22:32 24 4
gpt4 key购买 nike

我正在尝试理解 move 语义和复制/move 省略。

我想要一个包含一些数据的类。我想在构造函数中传递数据,我想拥有这些数据。

看完this , thisthis我的印象是,在 C++11 中,如果我想存储一个拷贝,那么按值传递应该至少与任何其他选项一样有效(除了增加代码大小的小问题)。

然后如果调用代码想避免复制,它可以通过传递右值而不是左值来实现。 (例如使用 std::move)

所以我试了一下:

#include <iostream>

struct Data {
Data() { std::cout << " constructor\n";}
Data(const Data& data) { std::cout << " copy constructor\n";}
Data(Data&& data) { std::cout << " move constructor\n";}
};

struct DataWrapperWithMove {
Data data_;
DataWrapperWithMove(Data&& data) : data_(std::move(data)) { }
};

struct DataWrapperByValue {
Data data_;
DataWrapperByValue(Data data) : data_(std::move(data)) { }
};

Data
function_returning_data() {
Data d;
return d;
}

int main() {
std::cout << "1. DataWrapperWithMove:\n";
Data d1;
DataWrapperWithMove a1(std::move(d1));

std::cout << "2. DataWrapperByValue:\n";
Data d2;
DataWrapperByValue a2(std::move(d2));

std::cout << "3. RVO:\n";
DataWrapperByValue a3(function_returning_data());
}

输出:

1. DataWrapperWithMove:
constructor
move constructor
2. DataWrapperByValue:
constructor
move constructor
move constructor
3. RVO:
constructor
move constructor

我很高兴在这些情况下都没有调用复制构造函数,但为什么在第二种情况下调用了一个额外的 move 构造函数?我想 Data 的任何像样的 move 构造函数都应该非常快,但它仍然让我烦恼。我很想改用按右值引用传递(第一个选项),因为这似乎会减少一次 move 构造函数调用,但如果可以的话,我想采用按值传递和复制省略。

最佳答案

DataWrapperByValue::data_ 是从 DataWrapperByValue::DataWrapperByValue(Data data) 的参数 data 中移出的,它是从 中移入的d2

对于获得左值的情况,您得出的按右值引用传递的结论以及按值版本会产生最佳性能。然而,这被广泛认为是过早的优化。 Howard Hinnant ( Best way to write constructor of a class who holds a STL container in C++11 ) 和 Sean Parent ( http://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil ) 都注意到他们认为这种过早的优化。原因是 move 应该非常便宜并且在这种情况下避免它们会导致代码重复,特别是如果您有多个参数可以是 r 或 l 值。如果通过概要分析或测试您发现这确实会降低性能,您总是可以在事后轻松添加右值传递引用。

在您确实需要额外性能的情况下,一个有用的模式是:

struct DataWrapperByMoveOrCopy {
Data data_;
template<typename T,
typename = typename std::enable_if< //SFINAE check to make sure of correct type
std::is_same<typename std::decay<T>::type, Data>::value
>::type
>
DataWrapperByMoveOrCopy(T&& data) : data_{ std::forward<T>(data) } { }
};

这里的构造函数总是做正确的事情,正如我的实例所示:http://ideone.com/UsltRA

这种有争议的复杂代码的优势可能与单个参数无关,但想象一下,如果您的构造函数有 4 个参数(可能是 r 值或 l 值),这比编写 16 个不同的构造函数要好得多。

struct CompositeWrapperByMoveOrCopy {
Data data_;
Foo foo_;
Bar bar_;
Baz baz_;
template<typename T, typename U, typename V, typename W,
typename = typename std::enable_if<
std::is_same<typename std::decay<T>::type, Data>::value &&
std::is_same<typename std::decay<U>::type, Foo>::value &&
std::is_same<typename std::decay<V>::type, Bar>::value &&
std::is_same<typename std::decay<W>::type, Baz>::value
>::type
>
CompositeWrapperByMoveOrCopy(T&& data, U&& foo, V&& bar, W&& baz) :
data_{ std::forward<T>(data) },
foo_{ std::forward<U>(foo) },
bar_{ std::forward<V>(bar) },
baz_{ std::forward<W>(baz) } { }
};

请注意,您可以省略 SFINAE 检查,但这会导致一些细微的问题,例如使用显式构造函数进行隐式转换。同样,在不检查参数类型的情况下,转换被推迟到构造函数内部,那里有不同的访问权限、不同的 ADL 等。请参见实际示例:http://ideone.com/yb4e3Z

关于c++ - 传递值导致额外 move ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22425679/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com