我正在使用C++ Primer第5版进行练习,如下所示:
练习13.50:将print语句放在String类的移动操作中, 并从第13.6.1节(第534页)中的练习13.48重新运行程序,该练习使用向量来查看何时避免复制.(P.544)
这String是一个练习的类,其行为类似于std::string不使用任何模板.该String.h文件中:
class String
{
public:
//! default constructor
String();
//! constructor taking C-style string i.e. a char array terminated with'\0'.
explicit String(const char * const c);
//! copy constructor
explicit String(const String& s);
//! move constructor
String(String&& s) noexcept;
//! operator =
String& operator = (const String& rhs);
//! move operator =
String& operator = (String&& rhs) noexcept;
//! destructor
~String();
//! members
char* …Run Code Online (Sandbox Code Playgroud) 我的问题涉及如何返回没有复制构造函数的对象.举一个例子,让我们想象一下,我有一些bigResource位于堆中,让我们说我用它来跟踪它unique_ptr.现在假设我将此资源的所有权交给了毛毛虫.然后我有一个CaterpillarWithBigResource.现在在某些时候,这CaterpillarWithBigResource将变成一个ButterflyWithBigResource,所以Caterpillar对象必须将所有权转移到Butterfly对象.
我编写了以下代码来模拟情况:
#include <cstdlib>
#include <iostream>
#include <memory>
class ButterflyWithBigResource {
public:
// If I uncomment just this line, I get an error
// ButterflyWithBigResource(const ButterflyWithBigResource& other) = default;
// If I uncomment just this line, I get an error
// ButterflyWithBigResource(const ButterflyWithBigResource& other) = delete;
// With both above lines commented out, I get no errors, and the program runs fine.
ButterflyWithBigResource(std::unique_ptr<int>&& bigResource) :
bigResource(std::move(bigResource)) …Run Code Online (Sandbox Code Playgroud) 下面的代码片段会调用复制构造函数,我希望调用移动构造函数:
#include <cstdio>
struct Foo
{
Foo() { puts("Foo gets built!"); }
Foo(const Foo& foo) { puts("Foo gets copied!"); }
Foo(Foo&& foo) { puts("Foo gets moved!"); }
};
struct Bar { Foo foo; };
Bar Meow() { Bar bar; return bar; }
int main() { Bar bar(Meow()); }
Run Code Online (Sandbox Code Playgroud)
在VS11 Beta上,在调试模式下,打印:
Foo gets built!
Foo gets copied!
Foo gets copied!
Run Code Online (Sandbox Code Playgroud)
我检查了标准,并且Bar似乎满足了自动生成默认移动构造函数的所有要求,但这似乎不会发生,除非有另一个原因导致无法移动对象.我在这里看到了很多移动和复制构造函数相关的问题,但我认为没有人遇到过这个具体问题.
关于这里发生了什么的任何指示?这是标准行为吗?
最小的工作示例.
#include <cassert>
#include <list>
#include <queue>
//#define USE_PQ
struct MyClass
{
const char* str;
MyClass(const char* _str) : str(_str) {}
MyClass(MyClass&& src) { str = src.str; src.str = nullptr; }
MyClass(const MyClass&) = delete;
};
struct cmp_func
{
bool operator() (const MyClass&, const MyClass&) const
{
return true;
}
};
typedef std::priority_queue<MyClass, std::vector<MyClass>, cmp_func> pq_type;
#ifdef USE_PQ
MyClass remove_front(pq_type& l)
{
MyClass moved = std::move(l.top());
// error from the above line:
// use of deleted function ‘MyClass::MyClass(const MyClass&)’
l.pop(); …Run Code Online (Sandbox Code Playgroud) 真实的例子显然要长得多,但这总结了我的问题:
class Object
{
int mInt1,mInt2;
Object::Object();
Object::Object(int param1);
Object::Object(int param1, int param2);
};
Object::Object(){}
Object::Object(int param1):mInt1(param1){}
Object::Object(int param1, int param2):mInt1(param1),mInt1(param2){}
Run Code Online (Sandbox Code Playgroud)
然后在主要:
if (type1){
Object instance(param1);
}
else{
Object instance(param1,param2);
}
// do stuff with instance
Run Code Online (Sandbox Code Playgroud)
哎呦!这不起作用,实例超出了后续程序的范围.
Object instance;
if (type1){
instance = Object(param1);
}
else{
instance = Object(param1,param2);
}
// do stuff with instance
Run Code Online (Sandbox Code Playgroud)
但是现在我遇到了麻烦,因为我没有定义复制构造函数.我真的不想写一个拷贝构造函数,因为我的实际类有几十个成员,其中许多是非基本类型,可能需要更多的工作来复制.
具体来说,我得到了
main.cpp: error: use of deleted function ‘Object& Object::operator=(Object&&)’
instance = Object(param1);
^
note: ‘Object& Object::operator=(Object&&)’ is implicitly deleted because the default definition would …Run Code Online (Sandbox Code Playgroud) 我有下课
class widget {
// The methods only print their name, i.e. c'tor, destructor etc.
public:
widget();
widget(const widget&);
widget(widget&&);
~widget();
auto operator=(const widget&) -> widget&;
auto operator=(widget&&) -> widget&;
};
Run Code Online (Sandbox Code Playgroud)
我在下面的代码中使用它
#include "widget.h"
auto main() -> int {
widget c(std::move(widget()));
c = std::move(widget());
return 0;
};
Run Code Online (Sandbox Code Playgroud)
由此产生的行为对我来说是可以理解的.在第一次调用中构造一个小部件,然后调用移动构造函数并在临时小部件上调用析构函数.
第二个调用也是这样,期望调用移动赋值运算符而不是移动构造函数.离开main方法,调用析构函数c.
现在有趣的是:
#include "widget.h"
auto main() -> int {
widget c((widget()));
c = widget();
return 0;
};
Run Code Online (Sandbox Code Playgroud)
如果我省略了调用std::move,第一个案例就会停止工作并导致只有一个构造函数调用.而第二个案件仍然像以前一样工作.
我在这里错过了什么?为什么这两个函数调用对待它们的参数有所不同?我在gcc和clang上尝试过这个.
与一起编译,/permissive但与一起失败/permissive-。什么不符合以及如何解决?
为什么很好,(2)但失败了?如果我删除它也很好。(4)(3)operator long
如何在不更改呼叫站点的情况下进行修复(3,4)?
#include <string>
struct my
{
std::string myVal;
my(std::string val): myVal(val) {}
operator std::string() { return myVal; };
operator long() { return std::stol(myVal); };
};
int main()
{
struct MyStruct
{
long n = my("1223"); // (1)
std::string s = my("ascas"); // (2)
} str;
str.s = my("ascas"); // (3)
str.n = my("1223"); // (4)
}
Run Code Online (Sandbox Code Playgroud)
错误信息
error C2593: 'operator =' is ambiguous
xstring(2667): note: could …Run Code Online (Sandbox Code Playgroud) c++ visual-c++ implicit-conversion move-constructor move-assignment-operator
我想检测(并使用 中的结果std::enable_if)C++ 类是否定义了移动构造函数。
以下程序打印MOVE,因此使用std::is_move_constructible不是这样做的方法:
#include <stdio.h>
#include <type_traits>
class C {
public:
C() { puts("C()"); }
C(int) { puts("C(int)"); }
~C() { puts("~C()"); }
C(const C&) { puts("C(const C&)"); }
// C(C&&) { puts("C(C&&)"); }
C& operator=(const C&) { puts("C="); return *this; }
};
int main(int argc, char** argv) {
(void)argc; (void)argv;
if (std::is_move_constructible<C>::value) puts("MOVE");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我需要一个程序,MOVE只有当我取消注释包含&&.
我正在阅读 Stroustrup 的“C++ v2 之旅”。这当然不是一本 C++ 初学者的书,但很有趣。
我用谷歌搜索了一下,但对这个没有任何兴趣。
现在,我想我明白编译器何时可以使用移动构造函数,但显然我不明白。在这里,我展示了移动构造函数以及我认为会使用它的函数。事实并非如此。仅当我明确使用 std::move 时。为什么是这样?我的理解是,本地r将在返回时隐式“移动”。
template<typename T>
Vector<T>::Vector(Vector<T> && a) // move constructor
:elem{a.elem},sz{a.sz}{
a.elem=nullptr;
a.sz=0;
}
template<typename T>
Vector<T> moveVectorAfterAdd(const Vector<T> & v1, const Vector<T> & v2){
Vector<T> r = v1+v2;
return std::move(r);
//return r;
}
int main(void) {
Vector<double> v1(1);
Vector<double> v2=v1;
Vector<double> v3=v2;
Vector<double> v4=moveVectorAfterAdd(v1,v2);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
(顺便说一句,如果我实际上不使用 std::move,尽管编译时没有进行任何优化,lldb 甚至不会让我在移动构造函数中设置断点。)
很高兴收到所有澄清!
move_constructible我从cppreference得到了以下 C++ 概念的实现
template<typename _Tp>
concept move_constructible =
constructible_from<_Tp, _Tp> &&
convertible_to<_Tp, _Tp>;
Run Code Online (Sandbox Code Playgroud)
我不明白为什么这有效。我认为任何类型都可以转换为自身,因此第二个要求是毫无意义的(上帝,我一定是在某些方面错了)。另外,对于第一个要求,我希望constructible_from<_Tp, _Tp&&>检查类型是否可以从 rvalue-ref 构造(因此移动)。
请解释一下这个实现是如何工作的。
c++ ×10
move-constructor ×10
c++11 ×8
visual-c++ ×2
c++-concepts ×1
c++20 ×1
constructor ×1
move ×1
scope ×1
stl ×1