使用一行代码清空C++中的stringstream:m.swap(std::stringstream());

5

我一直在使用以下代码来清除 std::stringstream

m.swap(std::stringstream());

这段代码可能来自这个SO帖子。最近我在Visual Studio 2013中编译我的代码时,收到了以下警告:

warning C4239: nonstandard extension used : 'argument' : conversion from 'std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>' to 'std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>> &'
1>          A non-const reference may only be bound to an lvalue

在较早的Visual Studio版本中使用/W4时我没有收到任何警告。我记得在较早的版本中,我有时无法可靠地清除stringstreams,这就是我最初使用那段代码的原因。

那么,用这种方式清除stringstream是否具有可移植性?如果不是,你能解释一下为什么吗?还有没有一种可移植的方法来清除stringstream?谢谢

编辑:这是所请求的代码。请使用/W4编译以查看警告。

int _tmain(int argc, _TCHAR* argv[])
{
    std::stringstream m;
    m.swap(std::stringstream());

    return 0;
}

我无法重现它,伙计,你能贴一些代码吗? - 101010
@Manu343726 放松点,伙计。 - 101010
我的VS++2013编译正常。 - 101010
我在VS2013中做了一个控制台应用程序,我运行了你发布的代码片段,没有进行任何优化,但我无法复现。它编译时没有警告。 - 101010
@40two 使用什么/W级别? - aschepler
显示剩余3条评论
3个回答

4

这里是 std::stringstream::swap() 的签名:

void swap( basic_stringstream& other );

正如您所看到的,swap() 接受其参数的左值引用。一个右值或临时变量无法绑定到左值引用上,正如您的编译器所正确告诉您的那样。有些编译器支持非标准扩展,允许您这样做,但我建议您遵循标准范围内进行操作,这样更加安全。

然而,虽然您可能无法将临时变量绑定到左值引用,但是可以使用std::skipws 技巧将临时变量 转换 为左值:

m.swap(static_cast<std::stringstream&>(stringstream{} >> std::skipws));

甚至

std::stringstream{}.swap(m):

在第一个示例中,函数operator>>()返回对流的引用,在使用swap()时是安全的,因为持有参数的引用在函数结束时被销毁。


@vsoftco 它到底是怎么不工作的?你收到了错误吗?如果有的话,是什么错误? - David G
以下是代码:#include #include using namespace std; int main() { stringstream m; m.swap(stringstream{} >> skipws); } - vsoftco
@vsoftco 是的,那是另一种解决方案。谢谢你提醒我。:D - David G
@0x499602D2 你能告诉我为什么需要使用 static_cast 吗?在我之前的理解中,>> 操作符返回的是一个引用,那么为什么需要再次进行类型转换呢?谢谢! - vsoftco
由于operator>>()返回的是*std::istream&*而不是std::stringstream&,因此我们需要将其向下转换为派生类的引用。 - David G
显示剩余7条评论

1
std::swap函数接受两个非const参数进行交换。这是因为在调用函数时两个参数都会被改变。同样,std::stringstream::swap(...)函数也接受一个非const参数进行交换。
例如:
std::vector<int> vec1, vec2;
std::swap(vec1, vec2);

在上面的示例中,vec1vec2都被改变。当你尝试这样做时...
m.swap(std::stringstream());

你试图将临时的 std::stringstream() 的内容与 m 的内容进行赋值,这在编译器看来可能是一个错误,即使你的意图只是清除 m 的内容。
为了消除警告,只需在执行交换操作之前声明一个临时的 std::stringstream。就像这样:
std::stringstream temp;
m.swap(temp);

3
先创建一个临时的stringstream对象,然后与成员变量m进行交换来清空m:std::stringstream().swap(m); - Billy ONeal
@BillyONeal - 的确,你也可以这样做! - Karl Nicoll
有一个类似的问题...请查看关于“lvalue”模板的评论。https://dev59.com/4X_aa4cB1Zd3GeqP7ses#23714506 - vsoftco
1
基本上,您正在将临时变量绑定到非const lvalue引用,这是不可能的。您必须使用C++11中的移动语义创建对临时变量的引用template<typename T> T& ref_from_temp(T&& x) {return x;},然后将m.swap(stringstream());替换为m.swap(ref_from_temp(stringstream())); - vsoftco
@vsoftco:不,你没有将一个临时变量绑定到非const左值引用上。唯一绑定到非const左值引用的是m,这是完全没问题的。 - Billy ONeal
显示剩余4条评论

1
那个使用swap的方式是不被标准允许的。声明为:
template <typename CharT, typename Traits, typename Allocator>
void std::basic_stringstream<CharT, Traits, Allocator>::swap(
    basic_stringstream& other);

这意味着两个stringstream对象都将被修改。但表达式std::stringstream()是一个rvalue(具体来说是临时值),C++不允许将非const lvalue引用绑定到临时值,因为修改临时值通常不是真正的意图。
简单的解决方案是:
m = std::stringstream();

但是,如果您需要支持没有C++11支持的编译器,则两者都无法使用,因为在C++03标准中,stringstream没有swap函数和公共operator=。 在这种情况下,您需要使用:
m.clear();
m.str(std::string());

你如何解释这个示例编译通过的事实? - 101010
.str() 的问题显然是它并不总是能够清除流,而我不记得为什么。 - user2672807
可能需要使用 m.clear() 进行完全重置,或者其他操作。仅使用 C++03 函数实现这一点比较困难。 - aschepler
好的,感谢您提供额外的信息,我很感激。我决定使用std::stringstream().swap(m);,因为即使它不是C++03,它在Visual Studio 2010和2013中也可以工作。我真的很想要更好的可移植性,但我不想冒险引入错误,如果需要的话,除了clear()和str(std::string())之外还需要更多的操作。 - user2672807

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接