关于将 string::swap() 与临时对象一起使用的问题

Posted

技术标签:

【中文标题】关于将 string::swap() 与临时对象一起使用的问题【英文标题】:Question about using string::swap() with temporaries 【发布时间】:2011-03-15 00:56:16 【问题描述】:

以下片段演示了我的问题:(GCC 上的编译错误)

stringstream ss;
string s;
ss << "Hello";

// This fails:
// s.swap(ss.str());

// This works:
ss.str().swap(s);

我的错误:

constSwap.cc:14: error: no matching function for call to 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >::swap(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
basic_string.tcc:496: note: candidates are: void std::basic_string<_CharT, _Traits, _Alloc>::swap(std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]

虽然我知道 stringstream 中的 str() 返回一个临时值,但它没有意义,也不是很明显我应该使用局部变量作为参数而不是我的第一直觉来调用临时值上的交换。

显然直接赋值效果更好,并且较新的 C++ 标准具有完美的移动语义,但这些不适用于我的实现。

Visual Studio 不存在这个问题,因为它对 C++ 标准放宽了。我已经理解了对临时事物的整个 const 引用(我认为这是我的编译错误的原因)。

我的问题: 如果这是唯一的解决方案,谁能向我解释一下,或许可以向我解释一下将来如何考虑这个问题,以便我可以发现并解决类似的问题?

(如果没有人有任何伟大的见解,我至少在这里为有类似问题的人发布此信息)

【问题讨论】:

另一个注意事项,要使交换工作,必须传入一个非常量引用。我所拥有的只是(因为它是临时返回)一个 const 右值。所以我明白了......它只是没有立即的逻辑意义,相反的方式是合法的(即使我也明白为什么会这样)。 你是在VS2010中编译代码吗,它有2个swap变种。一个是引用,另一个是右值引用。这可能是它正在编译的原因。 @Jagannath:我目前正在使用 VS2005,可惜,这些新技术对我来说都没有。 我想我了解 C++ 规范以及为什么会发生这种情况。正如有人所说:“这不是很地道”是我卡住的地方。对我来说,我并没有立即明白我可以颠倒这两个对象并且它是被允许的......这在惯用层面上没有意义。所以这不是一个直接的技术问题,虽然我很欣赏重新发布 C++ 规范的努力,但这并不是我在原始问题中所说的那样。如果我没有说得足够清楚,我很抱歉。我只是希望以后不会被一个简单的类似问题难住。 【参考方案1】:

您不能将临时对象绑定到非常量引用。由于这个原因,从ss.str() 返回的临时值不能传递给希望修改其参数的std::string::swap(因此它使用non-const&amp; 获取其参数)。

第二个版本的工作原理是您在允许的临时对象上调用成员函数。

但是你为什么首先要交换呢?通常,一个简单的:

std::string s(ss.str());

应该足够好了。这并不比交换效率低(至少在具有移动语义的 C++0x 中),但同时更具可读性。

【讨论】:

std::string s(s.str()) 比交换慢,因为它必须复制字符串(在 C++0x 中除外,它具有使用 &amp;&amp; 引用的移动语义)。【参考方案2】:

在使用 swap-with-temporary 成语足够多次之后,使用类似的行

std::vector<int>().swap(v); // clear and minimize capacity

std::vector<int>(v).swap(v); // shrink to fit

这似乎并没有那么不合适。 正常将 swap 作为临时对象的成员函数调用。当然,使用 swap 来填充默认构造的字符串而不是使用复制构造函数,这并不是那么惯用的,如前所述。

【讨论】:

这最好地解决了我的问题。我想我应该习惯这个成语。【参考方案3】:

您不能将临时作为参数传递给swap 的原因是该参数是通过非常量引用传递的。临时对象只能由 const-references 绑定。这分布在第 8.5.3 节中,适当的是第 5 段,第二个项目符号:

§8.5.3 对“cv1 T1”类型的引用由“cv2 T2”类型的表达式初始化,如下所示:

[要点一,此处不适用:绑定到非常量引用]

否则,引用应为非易失 const 类型(即 cv1 应为 const)。

以相反方向编写调用起作用的原因是标准允许在临时对象上调用变异成员函数。

§3.10/10 对象的左值是修改对象所必需的,但在某些情况下,类类型的右值也可用于修改其所指对象。 [示例:为对象(9.3)调用的成员函数可以修改该对象。 ]

你对未来的要求的推理是,虽然你可以通过它自己的函数修改一个临时的,但你不能将它传递给一个可以修改它的函数或方法(通过非常量引用传递)

【讨论】:

以上是关于关于将 string::swap() 与临时对象一起使用的问题的主要内容,如果未能解决你的问题,请参考以下文章

C++--临时对象与经典问题

关于何时使用临时对象来保存对对象的引用,而不是在每次使用时引用整个路径的经验法则? [关闭]

将具有关系的临时对象保存到 managedObjectContext 会导致错误 1550

DAY57-前端入门-javascript面向对象

Kotlin类与对象 ② ( 主构造函数 | 主构造函数定义临时变量 | 主构造函数中定义成员属性 | 次构造函数 | 构造函数默认参数 )

临时调用call()与apply()方法