c ++ 11 Thread类如何使用类成员函数

hys*_*ria 19 c++ c++11

我的程序如下所示

#include <iostream>
#include <thread>

class A {
public:
    void foo(int n ) { std::cout << n << std::endl; }
};

int main()
{
    A a;

    std::thread t1(&A::foo, std::ref(a), 100);

    t1.join();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我使用以下命令编译它时,我得到错误

g++ -o main main.cc -lpthread -std=c++11
Run Code Online (Sandbox Code Playgroud)

错误:

In file included from /usr/local/include/c++/4.8.2/thread:39:0,
                  from check.cc:2:
/usr/local/include/c++/4.8.2/functional: In instantiation of ‘struct std::_Bind_simple<std::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’:
/usr/local/include/c++/4.8.2/thread:137:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (A::*)(int); _Args = {std::reference_wrapper<A>, int}]’
check.cc:13:42:   required from here
/usr/local/include/c++/4.8.2/functional:1697:61: error?no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’
        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::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’
          _M_invoke(_Index_tuple<_Indices...>)
          ^
Run Code Online (Sandbox Code Playgroud)

Ker*_* SB 24

这不是参考包装器的正确位置.但是,一个简单的指针就足够了,并达到了预期的效果:

std::thread t1(&A::foo, &a, 100);
Run Code Online (Sandbox Code Playgroud)

  • +1为了得到正确的答案,而不是像某种白痴一样在GCC bugzilla上提交错误的错误. (6认同)

Cas*_*sey 11

编辑:撤回

Kerrek在这里是正确的:我错误地认为std::thread构造函数和std::bind设计相同的接口.然而,参数从自动转换reference_wrapper<A>A&仅用于指定std::bind在[func.bind.bind]/10:

绑定参数 的值v1, v2, ..., vN及其对应的类型V1, V2, ..., VN取决于TiD从调用派生的类型bind和调用包装器的cv -qualifiers cv,g如下所示:

  • 如果TiDreference_wrapper<T>,则参数为tid.get()且其类型ViT&;
  • ...

因此,这种特殊的使用reference_wrapper<A>被支持std::thread,但所支持std::bind.在其他/较旧编译器中与此实例std::thread相同的事实std::bind是错误,而不是4.8行GCC版本的行为.

我将在这里留下错误的答案,并希望其他人不会在将来犯同样的错误.

简短(但不正确)回答

这显然是GCC 4.8附带的标准库中的一个错误.代码正确编译:

很长(也很不正确)回答:

std::thread构造函数的效果

template <class F, class ...Args>
explicit thread(F&& f, Args&&... args);
Run Code Online (Sandbox Code Playgroud)

详见C++ 11 30.3.1.2 [thread.thread.constr]/4:

新的执行线程执行

INVOKE(DECAY_COPY(std::forward<F>(f)),
       DECAY_COPY(std::forward<Args>(args))...)
Run Code Online (Sandbox Code Playgroud)

调用DECAY_COPY在构造线程中进行评估.

DECAY_COPY 在30.2.6 [thread.decaycopy]/1中描述:

在本条款的几个地方使用了该操作DECAY_COPY(x).所有这些用法意味着调用函数decay_copy(x)并使用结果,其中decay_copy定义如下:

template <class T> typename decay<T>::type decay_copy(T&& v)
{ return std::forward<T>(v); }
Run Code Online (Sandbox Code Playgroud)

在OP中的调用中,std::thread t1(&A::foo, std::ref(a), 100);所有三个参数都是DECAY_COPY在调用之前将复制到新线程环境中的对象的rvalues ,其效果在20.8.2 [func.require]/1中描述:

定义INVOKE(f, t1, t2, ..., tN)如下:

  • (t1.*f)(t2, ..., tN)when f是指向类的成员函数的指针T,t1是类型T的对象T或对类型对象的引用或对派生类型的对象的引用T;
  • ((*t1).*f)(t2, ..., tN)when f是指向类的成员函数的指针,T并且t1不是上一项中描述的类型之一;
  • ...

对于OP中的代码,f是一个指向A带有值的类的成员函数的指针&A::foo,t1是一个reference_wrapper<A>存储引用引用的左值a,并且t2是一个int100.适用20.8.2/1的第二个子弹.既然t1是a reference_wrapper,则*t1计算存储的引用(按照20.8.3.3/1)并且新线程中的调用是有效的

(a.*&A::foo)(100);
Run Code Online (Sandbox Code Playgroud)

所以,是的,该标准完全按照预期描述了OP的行为.

编辑:奇怪的是,GCC 4.8正确编译了非常相似的例子:

class A {
public:
    void foo(int n) { std::cout << n << std::endl; }
};

int main()
{
    A a;
    auto foo = std::bind(&A::foo, std::ref(a), 100);
    foo();
}
Run Code Online (Sandbox Code Playgroud)


zah*_*hir 10

关于你的问题标题我将使用lambda进行线程构建.有或没有引用,通过调用成员函数或绑定参数.

 std::thread t1([&] { a.foo(100); });
Run Code Online (Sandbox Code Playgroud)


Jon*_*ely 7

GCC 4.8是正确的,std::thread根据INVOKE定义的其他组件不得以实现方式实现std::bind.它们不能调用嵌套的绑定表达式,并且必须对绑定参数使用完美转发(而不是将它们转发为lvalues std::bind),此外,如您发现它们不会解包reference_wrapper对象.在GCC 4.8中,我介绍了一个内部实现细节,__bind_simplestd::thread没有完整std::bind行为的等人使用.

虽然其他差异std::bind是可取的,但我认为INVOKE操作仍应支持reference_wrapper对象,因此我提交了一份缺陷报告,请参阅LWG 2219.

  • @juanchopanza,这是必须工作的.缺点是您要调用该成员的类对象不支持reference_wrappers,包装器对函数的参数工作正常. (2认同)