C++ 移动语义与常量引用
我的类有字符串变量,我想用传递给构造函数的值初始化它们。C++ 移动语义与常量引用,c++,string,reference,move-semantics,C++,String,Reference,Move Semantics,我的类有字符串变量,我想用传递给构造函数的值初始化它们。 我的老师认为我们应该传递字符串作为常量引用: MyClass::MyClass(const std::string和title){ 此->标题=标题 } 但是,我建议使用“移动”命令: MyClass::MyClass(std::string title){ 此->标题=标准::移动(标题) } 所以我想知道现代C++中正确的方法是什么? 我已经环顾四周,但没有什么真正回答我的问题。提前谢谢 复制引用会创建原始变量的副本(原始变量和
我的老师认为我们应该传递字符串作为常量引用:
MyClass::MyClass(const std::string和title){
此->标题=标题
}
但是,我建议使用“移动”命令:
MyClass::MyClass(std::string title){
此->标题=标准::移动(标题)
}
所以我想知道现代C++中正确的方法是什么?
我已经环顾四周,但没有什么真正回答我的问题。提前谢谢 复制引用会创建原始变量的副本(原始变量和新变量位于不同区域),移动局部变量会将局部变量强制转换为右值(同样,原始变量和新变量位于不同区域) 从编译器的角度来看,
move
可能(并且是)更快:
生成更大的代码(类似于GCC):
MyClass(std:uucx11::basic_ustring&):#@MyClass(std:uuucx11::basic_string&)
推送r15
推动r14
推送rbx
副区长,48
lea r15,[rsp+32]
mov qword ptr[rsp+16],r15
mov r14,qword ptr[rdi]
mov rbx,qword ptr[rdi+8]
测试r14,r14
jne.LBB0_2
测试rbx,rbx
约LBB0_11
.LBB0_2:
mov qword ptr[rsp+8],rbx
莫夫拉克斯,r15
cmp rbx,16
jb.LBB0_4
李尔迪[rsp+16]
lea rsi,[rsp+8]
xor edx,edx
调用std::u cxx11::基本字符串::_M_创建(无符号长&,无符号长)
mov qword ptr[rsp+16],rax
mov rcx,qword ptr[rsp+8]
mov qword ptr[rsp+32],rcx
.LBB0_4:
测试rbx,rbx
乙脑LBB0_8
cmp rbx,1
约LBB0_7
mov cl,字节ptr[r14]
mov字节ptr[rax],cl
jmp.LBB0_8
.LBB0_7:
莫夫尔迪,拉克斯
mov rsi,r14
mov-rdx,rbx
打电话给memcpy
.LBB0_8:
mov rax,qword ptr[rsp+8]
mov qword ptr[rsp+24],rax
mov rcx,qword ptr[rsp+16]
mov字节ptr[rcx+rax],0
mov rdi,qword ptr[rsp+16]
cmp-rdi,r15
乙脑LBB0_10
呼叫操作员删除(无效*)
.LBB0_10:
加上rsp,48
流行音乐
流行音乐r14
流行音乐r15
ret
.LBB0_11:
移动edi,偏移量L.str
调用标准::\抛出\逻辑\错误(字符常量*)
.L.str:
.asciz“基本\u字符串::\u M\u构造null无效”
因此,是的,
std::move
更好(在这些情况下)。可以使用常量引用,然后使用成员初始值设定项列表:
MyClass(const std::string &title) : m_title{title}
其中m_title是类中的成员字符串
您可以在这里找到有用的帮助:有两种情况:
std::string
的左值或右值
在std::string const&
版本中,lvalue大小写足够有效,通过引用传递,然后复制。但是右值将被复制,而不是移动,这样效率要低得多
在std::string
version中,传递时复制左值,然后移动到成员右值在这种情况下将移动两次。但一般来说,移动构造器是便宜的
MyClass(const std::string& title) : title(title) {} // A
template<typename... Args>
explicit MyClass(Args&&... args) : title(std::forward<Args>(args)...) {} // B
template<typename T>
explicit MyClass(T&& title) : title(std::forward<T>(title)) {}
此外,在std::string&
版本中,它不能接收左值,但右值是通过引用传递然后移动,比移动两次要好
所以很明显,它是
const&
和&
的最佳实践,就像STL总是做的那样。但是如果move构造函数足够便宜,那么只通过值传递和移动也是可以接受的。没有一个是最优的,因为它们都是先默认构造title
,然后复制分配或移动分配它。使用成员初始值设定项列表
MyClass::MyClass(const std::string& title) : title(title) {} // #1
// or
MyClass::MyClass(std::string title) : title(std::move(title)) {} // #2
//or
MyClass::MyClass(const std::string& title) : title(title) {} // #3
MyClass::MyClass(std::string&& title) : title(std::move(title)) {} // #3
让我们看看它们,看看在C++17中会发生什么:
#1-采用
常量的单个转换构造函数&
MyClass::MyClass(const std::string& title) : title(title) {}
这将通过以下方式之一创建1或2个std::string
s:
- 该成员是复制构造的
由std::string
转换构造函数构造,然后复制构造成员std::string
#2-单个转换构造函数按值获取
std::string
MyClass(std::string title) : title(std::move(title)) {}
这将通过以下方式之一创建1或2个std::string
s:
- 参数由临时(
+str1
)构造,然后成员被移动构造str2
- 参数是复制构造的,然后成员是移动构造的
- 参数是move构造的,然后成员是move构造的
- 参数由一个
转换构造函数构造,然后成员被移动构造std::string
#3-组合两个转换构造函数
MyClass(const std::string& title) : title(title) {}
MyClass(std::string&& title) : title(std::move(title)) {}
这将通过以下方式之一创建1或2个std::string
s:
- 该成员是复制构造的
- 该成员是移动构造的
由std::string
转换构造函数构造,然后移动构造成员std::string
到目前为止,选项3似乎是最有效的选项。让我们进一步检查一些选项
#4-与#3类似,但将移动转换构造函数替换为转发构造函数
MyClass(const std::string& title) : title(title) {} // A
template<typename... Args>
explicit MyClass(Args&&... args) : title(std::forward<Args>(args)...) {} // B
template<typename T>
explicit MyClass(T&& title) : title(std::forward<T>(title)) {}
这将始终创建1std::string
,就像在#4中一样,但所有操作都是通过转发构造函数完成的
MyClass(const std::string& title) : title(title) {} // A
template<typename... Args>
explicit MyClass(Args&&... args) : title(std::forward<Args>(args)...) {} // B
template<typename T>
explicit MyClass(T&& title) : title(std::forward<T>(title)) {}
- 会员是复印公司
template<typename T> explicit MyClass(T&& title) : title(std::forward<T>(title)) {}
template<typename T, typename U> MyClass(int X, T&& title, U&& title2) : x(X), title(std::forward<T>(title)), title2(std::forward<U>(title2)) {}