Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 移动语义与常量引用_C++_String_Reference_Move Semantics - Fatal编程技术网

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)) {}
这将始终创建1
std::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))
    {}