You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Effective Modern C++中setName函数临时std::string对象的疑问

关于《Effective Modern C++》setName两种实现的疑问解析

背景与代码示例

近期学习《Effective Modern C++11/14》第171页第25项时,作者给出了setName函数的两种实现示例:

实现1:万能引用版本

// 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");
}

实现2:左值+右值引用重载版本

// 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时,字符串字面量直接赋值给w的std::string成员,不会产生临时std::string对象;而重载版会先创建临时std::string对象绑定到参数,再将其移动到成员中。

个人疑问

我的理解是:两种实现都会在main中隐式为"Adela Novak"创建临时std::string,但选项1仅创建1个,选项2需创建2个,因此有以下三个技术疑问:

  • 为什么选项1不需要为newName创建额外的临时std::string对象?
  • 为什么选项2需要为newName创建额外的临时std::string对象?
  • 如何判断调用的成员函数是否会创建临时对象?

ChatGPT给出的回答

在选项1中,字符串字面量"Adela Novak"传入setName时,T被推导为const char*,newName为右值引用,std::move触发std::string的移动构造,仅创建1个临时std::string。

在选项2中,编译器先创建临时std::string匹配参数,再触发拷贝构造创建第二个临时对象,最终移动到成员中,共创建2个临时std::string。

疑问解答

1. 选项1无需额外临时对象的原因

万能引用的模板参数推导特性是核心:当传入字符串字面量"Adela Novak"(类型退化为const char*)时,模板参数T会被推导为const char*,此时newName的类型是const char* &&(右值引用)。

执行name = std::move(newName)时,std::move(newName)返回const char* &&,而std::string的赋值运算符支持直接接收const char*类型的右值,这个过程会直接用字面量初始化name的内部字符缓冲区,不需要额外创建临时std::string对象——本质是调用std::string& operator=(const char*),而非移动或拷贝构造。

2. 选项2需要额外临时对象的原因

选项2的重载函数参数均为std::string相关引用,但字符串字面量并非std::string类型,编译器必须先通过std::string(const char*)构造函数创建一个临时std::string对象,才能匹配到const std::string&或std::string&&参数。

无论最终匹配哪个重载,都必须先创建临时std::string来绑定函数参数,这就是作者所说的额外临时对象。后续的赋值操作(拷贝或移动)都是基于这个临时对象完成的。

3. 判断成员函数是否创建临时对象的方法

  • 检查类型匹配度:如果实参类型与函数参数类型(含推导后的引用类型)完全匹配,通常不会创建临时对象;若需要类型转换(如const char*转std::string),则必然会生成临时对象完成转换。
  • 追踪操作调用链:查看函数内部对参数的操作,若直接调用目标类型(如std::string)的构造/赋值运算符接收原始实参类型,则无临时对象;若参数是目标类型的引用,但实参不是目标类型,就需要先构造临时对象绑定参数。
  • 区分代码特性与编译器优化:RVO(返回值优化)等编译器优化可能消除临时对象,但这是编译器的行为,而非代码本身的特性,不能作为判断的依据,需以代码逻辑的固有行为为准。

内容的提问来源于stack exchange,提问作者Peter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 05:22:28