2

考虑以下示例代码:

#include <future>
#include <array>
#include <cassert>

typedef std::array<int, 5> foo_t;

foo_t* bar(foo_t& foo) {
   return &foo;
}

int main() {
   foo_t foo;
   auto a = std::async(bar, foo);
   auto b = std::async(bar, foo);
   assert(a.get() == b.get());
   return 0;
}

GCC 4.6.3 编译这个没有任何抱怨。但是,这在运行时失败:

test: test.cpp:15: int main(): Assertion `a.get() == b.get()' failed.
Aborted (core dumped)

然而,GCC 4.8.2 拒绝编译该文件:

In file included from /usr/local/include/c++/4.8.2/future:38:0,
                 from test.cpp:1:
/usr/local/include/c++/4.8.2/functional: In instantiation of 'struct std::_Bind_simple<std::array<int, 5ul>* (*(std::array<int, 5ul>))(std::array<int, 5ul>&)>':
/usr/local/include/c++/4.8.2/future:1525:70:   required from 'std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = std::array<int, 5ul>* (&)(std::array<int, 5ul>&); _Args = {std::array<int, 5ul>&}; typename std::result_of<_Functor(_ArgTypes ...)>::type = std::array<int, 5ul>*]'
/usr/local/include/c++/4.8.2/future:1541:36:   required from 'std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(_Fn&&, _Args&& ...) [with _Fn = std::array<int, 5ul>* (&)(std::array<int, 5ul>&); _Args = {std::array<int, 5ul>&}; typename std::result_of<_Functor(_ArgTypes ...)>::type = std::array<int, 5ul>*]'
test.cpp:13:30:   required from here
/usr/local/include/c++/4.8.2/functional:1697:61: error: no type named 'type' in 'class std::result_of<std::array<int, 5ul>* (*(std::array<int, 5ul>))(std::array<int, 5ul>&)>'
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/local/include/c++/4.8.2/functional:1727:9: error: no type named 'type' in 'class std::result_of<std::array<int, 5ul>* (*(std::array<int, 5ul>))(std::array<int, 5ul>&)>'
         _M_invoke(_Index_tuple<_Indices...>)
         ^

这似乎是一个 libstdc++ 问题。

所以我的问题是: 1 - GCC 是否应该拒绝此代码,或者标准中是否有我不知道的内容。2 - 断言应该失败吗?预期的行为是采用相同引用的异步函数应该引用相同的对象,但似乎副本是异步任务的本地副本。

我尝试使用 clang 进行编译,但它与 4.8.2 有相同的编译错误问题(因为它共享相同的 libstdc++),并且它无法编译 4.6.3 库头文件。

4

1 回答 1

5
  1. 是的,gcc 应该拒绝这个代码。std::async复制参数并将它们作为右值转发。您不能将右值绑定到左值引用,因此这会失败。如果您想通过引用传递,请使用std::ref(foo). 在这个具体的例子中,调用std::async(bar,foo)本质上做了以下事情:

    template<typename F>
    future<foo_t*> async(F& func,foo_t& arg){
        F func_copy(func);
        foo_t arg_copy(arg);
        // on background thread
        internal_set_future_result(func_copy(std::move(arg_copy)));
        return ...;
    }
    
  2. 如果你使用std::ref(foo),那么断言不应该失败。如果代码无法编译,那么这是一个有争议的问题。

于 2014-04-22T08:35:39.507 回答