Yves
Yves

Reputation: 12371

Is std::set<std::future> impossible to exist

I'm using C++11 to do some thread program.
Now I get such a situation:

I have a std::set<std::future<std::string>> results to store some results of threads, of course all of these threads will return a string.

However, when I try to get the strings, I get an error as below:

passing xxx as 'this' argument of xxx discards qualifiers

According to this link, I think it's because I'm trying to call a non-const function which belongs to the element of the set. In other words, the element of the set is std::future<std::string> and std::future<std::string>::get() is non-const. This is why I get such an error.

If I'm right, does it mean that I can never declare a std::set<std::future> because its get is always unusable?

Here is my code:

set<future<string>> results;
results.insert(...); // insert some future
for(auto it = results.begin(); it != results.end();)
{
    if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
    {
        string tmp = it->get();  // ERROR!!!
    }
}

Upvotes: 0

Views: 252

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275405

When thinking of what container to use, you should think "can I do this with a std::vector?". If your answer is no, you should ask "no, really, can I do this with a std::vector?".

using namespace std::literals;
std::vector<std::future<std::string>> results;
constexpr auto threads = 10;
for (int i = 0; i < threads; ++i) {
  results.push_back( std::async( std::launch::async,
    [i]{
      std::this_thread::sleep_for( std::chrono::milliseconds((threads-i)%(threads/3)) );
      std::stringstream ss;
      ss << "hello world " << i;
      return ss.str();
    }
  ) );
}
for (auto it = results.begin(); it != results.end();) {
  if (it->wait_for( 0s ) == std::future_status::ready) {
    std::string tmp = it->get();
    std::cout << tmp << "\n";
    std::swap( *it, results.back() );  // noop if already at back
    if (std::next(it)==results.end()) it = results.begin(); // do this before pop back
    results.pop_back(); // Or this could invalidate it
    if (results.begin()==results.end()) break; // empty vector has to be caught early, as it is invalidated if vector is now empty
    continue;
  } else {
    ++it;
    if (it == results.end()) it = results.begin();
    continue;
  }     
}

Live example

Doing this with a std::set is both a bad and impossible idea.

A bad idea, because std::set is a node based container that does effort to maintain sort order. We don't need nodes. We don't need a sort order. So we are using a more powerful container than we need.

Impossible, because the contents of std::set are by design immutable.

Impossible, because std::future provides no operator<, nor is there a reasonable place to hook a user-provided comparison function onto them.

Note that shared_futures have a const get() method, so they can be stored in an immutable manner. But they still provide no "hooks" for a user-provided ordering operation.

Upvotes: 5

Related Questions