Why can I call boost::unique_future::get many time

2019-05-25 01:42发布

问题:

I know we can't call std::future::get many times, and we should use std::shared_future if we need to call it many times.

But we can call boost::unique_future::get many times, although there's boost::shared_future!

void test1()
{
    int i, j;

    std::future<int> fu1 = std::async([]{ return 42; });
    i = fu1.get();
    //j = fu1.get(); // error occur
    std::cout << i << std::endl;

    boost::unique_future<int> fu2 = boost::async([]{ return 43; });
    i = fu2.get();
    j = fu2.get(); // sucess...?
    std::cout << i << ' ' << j << std::endl;

    std::cin.get();
}

The output is:

42
43 43

I thought for a moment and then try this test code.

class TestCls
{
public:
    TestCls()
    {
        std::cout << "[TestCls] default constructor" << std::endl;
    }
    TestCls(const TestCls &other)
    {
        std::cout << "[TestCls] copy constructor" << std::endl;
    }
    TestCls(TestCls &&other)
    {
        std::cout << "[TestCls] move constructor" << std::endl;
    }
    TestCls &operator =(const TestCls &other)
    {
        std::cout << "[TestCls] copy assignment" << std::endl;
        return *this;
    }
    TestCls &operator =(TestCls &&other)
    {
        std::cout << "[TestCls] move assignment" << std::endl;
        return *this;
    }
};
void test2()
{
    TestCls a, b;

    std::cout << std::endl << "unique_future test begin" << std::endl;
    boost::unique_future<TestCls> fu1 = boost::async([]{ return TestCls(); });
    fu1.wait();
    std::cout << "first assignment" << std::endl;
    a = fu1.get();
    std::cout << "second assignment" << std::endl;
    b = fu1.get();
    std::cout << "unique_future test end" << std::endl;

    std::cout << std::endl << "shared_future test begin" << std::endl;
    boost::shared_future<TestCls> fu2 = boost::async([]{ return TestCls(); });
    fu2.wait();
    std::cout << "first assignment" << std::endl;
    a = fu2.get();
    std::cout << "second assignment" << std::endl;
    b = fu2.get();
    std::cout << "shared_future test end" << std::endl;

    std::cin.get();
}

The output is:

[TestCls] default constructor
[TestCls] default constructor

unique_future test begin
[TestCls] default constructor
[TestCls] move constructor
first assignment
[TestCls] move constructor
[TestCls] move assignment
second assignment
[TestCls] move constructor
[TestCls] move assignment
unique_future test end

shared_future test begin
[TestCls] default constructor
[TestCls] move constructor
first assignment
[TestCls] copy assignment
second assignment
[TestCls] copy assignment
shared_future test end

Although boost::unique_future do "move", not "copy", it is allowed to call get many times.. How is it possible?

(My boost version is 1.55.0, and my compiler is VC++ 2013)


If I do #define BOOST_THREAD_VERSION 4, an exception occurs at the second call of get(). multiple call of get() is undefined behavior until version 3? Or it's allowed until version 3?

回答1:

From the documentation it's clear that there are at least 3 macro that are playing a significant role in what you are using

BOOST_THREAD_VERSION

which sets the version of the library, even if there are breaking changes between 4 and 3, you don't seem to have a problem with that.

second macro is BOOST_THREAD_PROVIDES_FUTURE, this macro is a switch, no value attached, if defined it enables the "standard" futures, not the unique_futures, what you are calling unique_future is just a placeholder for something that is defined by this macro at compile time.

from the file boost/thread/future.hpp

#if defined BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_FUTURE future
#else
#define BOOST_THREAD_FUTURE unique_future
#endif

in the file boost/thread/detail/config.hpp you also have BOOST_THREAD_DONT_PROVIDE_FUTURE which is another switch for default actions

// PROVIDE_FUTURE
#if ! defined BOOST_THREAD_DONT_PROVIDE_FUTURE \
 && ! defined BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE
#endif

note the negation !, which means that if you define BOOST_THREAD_DONT_PROVIDE_FUTURE you should get "real" unique_future as documented by the library.

You are basically getting the default behaviour and the fact that you are using the version 4 of the library doesn't matter that much.

A side note about C++11 and beyond:

std::async([]{ return 42; });

this is bad practice in C++11 and it's undefined behaviour in C++14, you should always specify a launch policy for your async.