我已经知道自动移动不会与从右值参考输入返回值的函数一起唤醒。但为什么?
下面是自动移动不起作用的示例代码。
Widget makeWidget(Widget&& w) {
....
return w; // Compiler copies w. not move it.
}
Run Code Online (Sandbox Code Playgroud)
如果函数输入来自按值复制,则自动移动有效。
Widget makeWidget(Widget w) {
....
return w; // Compiler moves w. not copy it.
}
Run Code Online (Sandbox Code Playgroud) 由于std::atomic
复制构造函数被删除,并且由于复制省略,这应该只能使用 C++17 及更高版本进行编译:
std::atomic<int> t_int = 1;
Run Code Online (Sandbox Code Playgroud)
我预计它不会使用-fno-elide-constructors
标志进行编译,但它仍然可以编译:
https://godbolt.org/z/nMvG5vTrK
为什么是这样?
假设有一个用户定义的class Foo
。一些帖子表明 C++ 类对象“永远”不会在堆上分配,除非使用new
. 但!另一方面,有一些帖子建议从函数中按值返回本地外观的类对象不一定会复制任何数据。所以!这样一个对象的数据首先存储在哪里?还在堆吗?它是否被提升到调用函数的堆栈帧?
class Foo {
...
}
Foo a(int x) {
Foo result;
doabc(x, result);
return result;
}
Foo b(int x) {
Foo result = a(x);
doxyz(x,result);
return result;
}
int main() {
int x;
cin >> x;
Foo result = b(x);
dosomethingelse(result);
cout << result;
}
Run Code Online (Sandbox Code Playgroud)
如果 a 的 Foo 结果不是按值复制的,那么它在哪里分配?堆还是栈?如果在堆上,编译器是否会自动重构代码以插入删除?如果在堆栈上,它会驻留在哪个堆栈帧上?b 的?这是让我想知道的帖子:/sf/answers/1223171211/。谢谢!
2011年,该补丁中提交了对Eigen的移动支持.但是我在Matrix头文件中找不到移动构造函数.此外,Eigen网页仍然将移动语义列为"待办事项".所有这些表明补丁尚未提交到发布版本
此测试程序证明移动构造函数保持移动的对象不变.
#include <iostream>
#include <utility>
#include <eigen3/Eigen/Dense>
int main()
{
Eigen::VectorXd first = Eigen::VectorXd::Constant(3, 3.14);
std::cout << "first\n" << first << std::endl << std::endl;
Eigen::VectorXd other = std::move(first);
std::cout << "first\n" << first << std::endl << std::endl;
std::cout << "other\n" << other << std::endl << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
first
3.14
3.14
3.14
first
3.14
3.14
3.14
other
3.14
3.14
3.14
Run Code Online (Sandbox Code Playgroud)
如何从Eigen 3.2.0中的上述补丁中启用复制删除移动功能.?
编辑:
似乎特征类的移动语义没有任何问题.但是,只有 …
我知道C++ 11从这个链接中移动了语义: 现代C++ Style的元素
但它没有介绍如何使用移动语义返回向量.这该怎么做?
尝试使用不同的文件名创建一些gzip存档我写下以下代码片段.
#include <iostream>
#include <utility>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filter/gzip.hpp>
boost::iostreams::filtering_ostream&& makeGZipStream(const std::string& archiveName,
const std::string& fileName)
{
boost::iostreams::filtering_ostream theGzipStream;
boost::iostreams::gzip_params theGzipParams;
theGzipParams.file_name = fileName;
theGzipStream.push(boost::iostreams::gzip_compressor{theGzipParams});
theGzipStream.push(boost::iostreams::file_sink{archiveName});
return std::move(theGzipStream);
}
int main()
{
boost::iostreams::filtering_ostream&& theGzipStream = makeGZipStream("archive.gz", "file");
theGzipStream << "This is a test..." << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这(我们可以预期)会产生核心转储,因为makeGZipStream
我们尝试通过(rvalue-)引用返回本地堆栈分配的变量.但在这种情况下,副本不是一个选项,因为它boost::iostreams::filtering_ostream
是不可复制的.
std::unique_ptr
"按值"(由于copy-elision,这个移动甚至不应该出现在C++ 17中),为什么在这种情况下不可能呢?unique_ptr
(不那么漂亮)使用的编译器很老了g++ (GCC) 4.9.3
.
有人可以向我解释一件事吗?从一方面来看,它的move constructor
设计目的是通过消除不必要的复制对象来优化内存和处理器的使用,但从另一面来看,几乎所有地方move constructor
都会使用编译器使用复制省略move ctor
,禁用?的使用。这不是不合理吗?
如果我们写如下函数:
auto foo() {
Foo foo { /* ... */ };
do_stuff(foo);
return foo;
}
Run Code Online (Sandbox Code Playgroud)
然后NRVO应该启动,这样foo
就不会在返回时被复制。
现在假设我想返回两个不同的值:
auto foo() {
Foo foo { /* ... */ };
Bar bar { /* ... */ };
do_stuff(foo, bar);
return std::make_tuple(foo, bar);
}
Run Code Online (Sandbox Code Playgroud)
这种幼稚的实现可能会触发构建每个Foo
和Bar
( GodBolt)的两个副本。
我应该如何最好地修改我的代码以避免这种复制,而不会弄乱我的返回类型?
请考虑以下 C++17 代码:
#include <iostream>
#include <optional>
struct S
{
S(int) { std::cout << "S() "; }
S(const S &) { std::cout << "S(const S &) "; }
S(S &&) = delete;
~S() { std::cout << "~S() "; }
};
int main()
{
[[maybe_unused]] std::optional<S> v = true ? std::optional<S>(1) : std::nullopt;
}
Run Code Online (Sandbox Code Playgroud)
在带有 /std:c++latest 选项 (C++20) 的最新 Visual Studio 2019 16.10.3 中,它会打印
S() S(const S &) ~S() ~S()
Run Code Online (Sandbox Code Playgroud)
即使在具有优化的发布配置中。
即使没有优化,GCC 和 Clang 输出也是不同的(https://gcc.godbolt.org/z/ofGrzhjbc)
S() ~S()
Run Code Online (Sandbox Code Playgroud)
复制省略在这里是可选的(所有编译器都在他们的权利范围内),还是这里不允许复制省略(只有 MSVC …
#include <iostream>
class A {
public:
A() { std::cout << "Constructor" << std::endl; }
A(const A& a) { std::cout << "Copy Constructor" << std::endl; }
A& operator=(const A& a) { std::cout << "Copy = operator" << std::endl; }
A(A&& a) { std::cout << "Move Constructor" << std::endl; }
A& operator=(A&& a) { std::cout << "Move = operator" << std::endl; }
~A() { std::cout << "Destructor" << std::endl; }
};
void f(A&& a) { std::cout << "function" << std::endl; }
int …
Run Code Online (Sandbox Code Playgroud) 鉴于:
//C++17
#include <string>
struct Foo {
int i;
std::string str;
};
int main() {
Foo foo{1, std::string("Hello, world!")};
}
Run Code Online (Sandbox Code Playgroud)
可以Foo::i
直接Foo::str
从1
和std::string(...)
初始化,而不是复制到其中,并解释为什么可以/不能使用 C++17 标准(可能是一些用于测试目的的代码)?
如果不能,需要多少份?
为什么标准允许我的编译器应用复制省略,即使它涉及可见的副作用,从而打破了 as-if 规则?
当一个人保证复制省略时,这对我来说是有道理的,因为复制/移动的实际功能(这将调用程序行为的可见变化)不一定存在,但为什么/如何在 C+ 之前+17?
是不是因为编译器一般不能检测副作用(不知道是否可行)?
有这个简单的类:
#include <iostream>
class B
{
public:
//default constructor
B(const char* str = "\0") {
std::cout << "Constructor called\n";
}
//copy constructor
B(const B& b) {
std::cout << "Copy constructor called\n";
}
//move constructor
B(B&& b) {
std::cout << "Move constructor called\n";
}
};
Run Code Online (Sandbox Code Playgroud)
这些语句之间在移动语义方面有什么区别:
B o1 = B("abc");
B o2 = B(B("abc"));
Run Code Online (Sandbox Code Playgroud)
这两行等价吗?
c++ ×13
copy-elision ×13
c++11 ×4
c++17 ×3
constructor ×2
as-if ×1
eigen ×1
move ×1
noncopyable ×1
nrvo ×1
stack-frame ×1
stdmove ×1