Qiang Li
Qiang Li

Reputation: 10855

c++ How to save the use of a temporary variable

I have the following code in C++:

string str="a b c";
stringstream sstr(str);
vector<string> my_vec((istream_iterator<string>(sstr)), 
                       istream_iterator<string>());

Is there any way to save the use of sstr, something like the following?

vector<string> my_vec((istream_iterator<string>(str)), 
                       istream_iterator<string>());

Upvotes: 0

Views: 399

Answers (3)

Matthieu M.
Matthieu M.

Reputation: 299790

Boost has a library dedicated to algorithm on string: check out the Split section :)

std::vector<std::string> vec;
boost::split(vec, "a b c", boost::is_any_of(" ")); 
  // vec == { "a", "b", "c" }

Probably the clearest way to do it :)

Upvotes: 0

Mike DeSimone
Mike DeSimone

Reputation: 42805

You're using the two-iterator constructor for vector with istream_iterator to split the string by whitespace into a sequence of strings to be stored.

istream_iterator needs an istream, for which there is no direct cast from string. The compiler is not going to infer a stringstream because the constructor for istream_iterator takes a templated type and not explicitly a stringstream. It's just too much of a leap for a compiler to assume that much.

Besides, even if the compiler made such a leap of faith, it would generate the same code as what you already have, so you're no better off in the end.

A better approach might be:

std::vector<std::string> split_words(const std::string& str)
{   size_t offset = str.find_first_not_of(" \t\r\n");
    std::vector<std::string> result;
    while(offset != std::string::npos)
    {   size_t end = str.find_first_of(" \t\r\n", offset);
        if(end != offset)
            result.push_back(std::string(str, offset, end));
        offset = str.find_first_not_of(" \t\r\n", end);
    }
    return result;
}

which takes less code and objects to get the same job done. On my Mac, this is 3203 bytes code and 273 data, while the original three lines of code is 5136 bytes code and 353 data. (I added return my_vec.size(); at the end of main().)

Upvotes: 0

GManNickG
GManNickG

Reputation: 503815

istream_iterator's argument needs to be able to bind to a non-const reference, and a temporary cannot. However, (as Alf points out), ostream happens to have a function, flush(), that returns a non-const reference to itself. So a possibility is:

string str="a b c";
vector<string> my_vec(istream_iterator<string>(
                        static_cast<stringstream&>(stringstream(str).flush())
                        ), istream_iterator<string>());

Though that's an eye-sore. If you're concerned about having too many lines, then use a function:

vector<string> string_to_vector(const string& str)
{
    stringstream sstr(str);
    return vector<string>(istream_iterator<string>(sstr),
                            istream_iterator<string>());
}

Giving:

string str="a b c";
vector<string> my_vec = string_to_vector(str);

This is even cleaner than what you'd get even if you could shorten your code, because now what is being done is not expressed in code but rather the name of a function; the latter is much easier to grasp.


*Of course, we can add boiler-plate code to do silly things:

class temporary_stringstream
{
public:
    temporary_stringstream(const string& str) :
    mStream(str)
    {}

    operator stringstream&()
    {
        // only persists as long as temporary_stringstream!
        return mStream;
    }

private:
    stringstream mStream;
};

Giving:

string str="a b c";
vector<string> my_vec((istream_iterator<string>(temporary_stringstream(str))),
                        istream_iterator<string>());

But this is just as ugly as the first solution.

Upvotes: 4

Related Questions