我最近一直在学习Effective Modern C++11和14。
我浏览了本书第171页第25项的一部分,其中作者提供了setName函数的两个代码示例。
(一)
// Option 1, universal reference
class Widget {
public:
template<typename T>
void setName(T &&newName) // universal reference
{ name = std::move(newName); }// compiles, but is // bad, bad, bad!
//…
private:
std::string name;
std::shared_ptr<SomeDataStructure> p;
};
void main() {
Widget w;
w.setName("Adela Novak");
}
(二)
// Option 2, lvalue reference and rvalue reference
class Widget2 {
public:
void setName(const std::string &newName) { name = newName; }
void setName(std::string &&newName) { name = std::move(newName); }
//…
};
void main() {
Widget2 w;
w.setName("Adela Novak");
}
作者说:
由于setName的版本采用了通用引用,字符串文字“Adela Novak”将被传递给setName,在那里它将被传递给w中std::string的赋值运算符。w的名称数据成员因此将直接从字符串文字分配;则不会出现临时std::string对象。
然而,对于重载版本的setName,将为setName的参数创建一个临时的std::string对象,然后将这个临时的std::string移动到w的数据成员中。因此,对setName的调用将需要执行一个std::string构造函数(以创建临时)、一个std::string移动赋值操作符(以将newName移动到ww. example) www.example.com )和一个std::string析构函数(用于销毁临时对象)。
如果我的理解是正确的,在选项1和选项2中,我们将在main()函数中隐式地为“Adela Novak”创建一个临时的std::string对象。
对于选项1,我们将临时std::string对象发送给setName(),然后T&&将被推导为右值,因为“Adela Novak”是右值。
因此,在选项1中,我们只创建一个临时的std::string对象。
对于选项2,我们将临时std::string对象发送到setName()。因为“Adela Novak”是右值,所以我们将使用右值重载setName()函数。但是,当我们要调用rvalue重载函数时,我们必须为newName显式地创建另一个临时std::string对象。
因此,在选项2中,我们必须创建两个临时std::string对象。
我的问题是:
1.为什么我们不必在选项1中为newName创建另一个临时std::string对象?
1.为什么我们必须在选项2中为newName创建另一个临时std::string对象?
1.如何知道我们要调用的成员函数是否会创建临时对象?
更新:我向ChatGPT提问。然而,ChatGPT提供的答案似乎不是100%作者的答案。..
在选项1中,当字符串文字“Adela Novak”传递给setName()函数时,T的类型推导导致T被推导为const char* 指针。由于T是非引用类型,newName被声明为右值引用(T&&
)。
当右值引用newName
被传递给std::move()
函数时,它执行一个转换为右值,这允许它绑定到std::string
类的移动构造函数。因此,临时std::string
对象是使用std::string
构造函数创建的,该构造函数接受const char*
参数。
然后使用移动赋值运算符将这个临时std::string
对象移动到name
数据成员中,从而不会再复制字符串数据。
因此,在选项1中,创建了一个临时std::string
对象来保存字符串数据,但不创建其他临时对象。
在选项2中,当字符串文字“Adela Novak”传递给setName()
函数时,编译器会尝试为参数找到最佳匹配。
第一个接受const std::string&
参数的setName()
函数是匹配的,因为它可以绑定到从字符串字面量创建的临时std::string
对象。因此,临时std::string
对象是使用接受const char*
参数的std::string
构造函数创建的。
接下来,临时std::string
对象通过引用传递给setName()
函数,这会触发std::string
的复制构造函数来创建临时对象的副本。
最后,使用移动赋值运算符将临时std::string
对象移动到name
数据成员,这将导致临时对象被销毁。
因此,在选项2中,创建了两个临时std::string
对象,一个用于保存字符串数据,另一个用于绑定到重载setName()
函数的std::string&&
参数。
1条答案
按热度按时间relj7zay1#
作者很清楚选项1:
[..]string literal“Adela Novak”将被传递给setName,在那里它将被传递给w中std::string的赋值运算符。w的名称数据成员因此将直接从字符串文字分配;不会出现临时std::string对象。
字符串文字不是
std::string
,而是一个char数组(const char[]
)。因此,通用引用允许您传递 * 任何类型 * 的对象,而无需任何转换或临时对象。至于选项2,可能会创建也可能不会创建临时对象,这取决于您在
setName
中传递的内容。如果传递std::string
,则不会创建临时文件。如果传递一个字符串文字,则需要先创建一个临时std::string
,然后传递对它的引用。