3

I have a question about the following code that tokenizes a string (separates the tokens by space).

#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

int main()
{
    string s="And I feel fine ...";
    istringstream ss(s);
    vector<string> tokens{istream_iterator<string>(ss),{}};
    for(auto& elem: tokens)
        cout << elem << endl;
}

This works perfectly fine. On the other hand, if I try to pass a temporary istringstream object to istream_iterator (3-rd line inside main), such as

vector<string> tokens{istream_iterator<string>(istringstream(s)),{}};

I am getting a compile-time error error: no matching conversion for functional-style cast from 'istringstream' (aka 'basic_istringstream<char>') to 'istream_iterator<string>'

I believe it is because I cannot bound a temporary rvalue to a non-const lvalue reference, and the constructor of istream_iterator takes a reference as a parameter. Is there any way of constructing an istream_iterator<string> from a temporary? I cannot use std::ref on a temp object either...

Thanks!

vsoftco
  • 55,410
  • 12
  • 139
  • 252

1 Answers1

4

Use the std::skipw trick:

std::vector<std::string> tokens{
    std::istream_iterator<std::string>(std::istringstream(s) >> std::skipws), {}
};

This works because the extractor returns an lvalue-reference to the stream, and setting std::skipw won't have any effect because it is enabled by default on all streams. To use this in the general case, you would want to have a function that takes a universal reference, then returns a reference to that object:

template<typename T>
T& lvalue(T&& x)
{
    return x;
}

// ...
std::istream_iterator<std::string>(lvalue(std::istringstream(s)));

Make sure the reference isn't being used past the full expression, or else you'll have a dangling reference as the temporary will have already been destructed.

David G
  • 94,763
  • 41
  • 167
  • 253
  • Nice! Still have a lot to learn about C++... For some reason I thought `std::ref` does exactly what your `lvalue` does, but it looks like the move constructor of `std::ref` is deleted, and it takes only references as parameters, so you cannot pass a temp into it. I find it extremely strange that the move ctor is deleted, do you know the reason why? – vsoftco May 17 '14 at 18:10
  • @vsoftco `std::ref` uses reference-semantics, not move-semantics. It only purpose is wrap a reference around an object, not to own the object. Moreover, `std::ref` is not meant to be used with temporaries because of the obvious dangling reference problem as I stated. – David G May 17 '14 at 18:17
  • @0x499602D2 ok it makes sense from a safety-like point of view... The `std::skipw` trick is really neat though.. thanks! – vsoftco May 17 '14 at 18:20