std :: thread带有指向数据成员的指针 [英] std::thread with pointer to data member

查看:943
本文介绍了std :: thread带有指向数据成员的指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读通过 std :: thread文档cppreference (不总是100%准确,我知道),并注意到当传递指针到数据成员(非指针到数据成员)时, std :: thread 成员函数)作为其第一个参数( f )和所需类的对象作为其第二个参数( t1 复制到线程本地存储之后):


如果N == 1并且f是指向类的成员数据对象的指针,然后进行访问。将忽略对象的值。有效地,执行以下代码:
t1。* f if和t1的类型是T,引用T或引用从T导出的类型。
(* t1)。* f否则。


现在,我不打算使用 std :: thread 这种方式,但我被这个定义flummoxed。显然,发生的唯一的事情是,数据成员被访问和忽略的值,似乎不可能有任何可观察到的副作用,意思(我可以告诉),它可能是一个无操作。 (我可能会缺少明显的东西...?)



首先,我认为这可能是一个错误印记,意味着数据成员被访问,然后因为它可能是一个可调用的对象,即使它不是一个函数),但我测试了它与GCC-4.7中的以下代码,确实没有调用:

  #include< iostream> 
#include< thread>

struct S
{
void f(){
std :: cout< 调用f()< std :: endl;
}

struct {
void operator()(){
std :: cout< calling g()<< std :: endl;
}
} g;
};

int main(int,char **)
{
S s;
s.f(); // printscalling f()
s.g(); // printscalling g()

std :: cout<< ----<< std :: endl;

auto x =& S :: f; // ptr-to-mem-func
auto y =& S :: g; // ptr-to-data-mem

(s。* x)(); // prints调用f()
(s。* y)(); // printscalling g()

std :: cout<< ----<< std :: endl;

std :: thread t(x,& s);
t.join();
//calling f()print by now
std :: thread u(y,& s);
u.join();
//未调用g()

return 0;
}

这个定义有什么目的似乎没有完成什么?为什么不让传递一个指针到数据成员可调用的行为就像一个指针到成员函数,并传递一个指针到数据成员不可调的错误?事实上,这似乎是实现它的最简单的方法,因为在其他上下文中调用指向数据成员可调用的指针具有与调用作为指针到成员函数的等效语法除非在模板专业化和SFINAE规则的迷幻中有某些东西使得难以等同地对待它们...?)



这不是我需要的实际代码,但是这个定义存在的事实让我怀疑我缺少一些基本的东西,这让我担心...任何人都可以告诉我这个吗?

解决方案

这是因为通用的绑定功能,C ++ 11标准不仅定义了线程的启动方式,还定义了 std :: bind std :: function 工作。



其实,第30.3.1.2/3节C + +11 Standard指定关于 std :: thread 的可变参数构造函数:


template< class F,class ... Args>显式线程(F&& F,Args& ... args);



构造一个线程类型的对象。新的执行线程执行 INVOKE(DECAY_-
COPY(std :: forward F(f)),DECAY_COPY(std :: forward
,其中在构造线程中评估对
DECAY_COPY 的调用。此调用的任何返回值都是
。 [...]


忽略 DECAY_COPY 对于问题),这是第20.8.2段如何定义 INVOKE 伪函数:


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



- (t1。* f )(t2,...,tN),当f是指向类T的成员函数的指针,t1是
类型T的对象或对类型T的对象的引用或对对象的引用从t开始的类型;当f是指向成员函数的指针时,



- ((* t1)* f)(t2,...,tN)的类T和t1不是以上项中描述的类型中的
之一;



- t1。* f当N == 1且f为指向类T和t1的成员数据的指针是类型T的对象或对类型T的对象的
引用或对从T派生的类型的对象的引用;

$ b $当N = 1且f是指向类T的成员数据的指针时,b

- (* t1)。* f,并且t1不是在上面描述的类型
中的一个

- f(t1,t2,...,tN) / blockquote>

现在问题变成了:


为什么C ++ 11标准定义 INVOKE 设施?


a href =http://stackoverflow.com/questions/12638393/why-does-invoke-facility-in-the-c11-standard-refer-to-data-members>此处


I was reading through the std::thread documentation at cppreference (not always 100% accurate, I know) and noticed the following definition for the behavior of std::thread when passed a "pointer-to-data-member" (not "pointer-to-member-function") as its first argument (f) and an object of the required class as its second argument (t1 after copying to thread-local-storage):

If N == 1 and f is pointer to a member data object of a class, then it is accessed. The value of the object is ignored. Effectively, the following code is executed: t1.*f if and the type of t1 is either T, reference to T or reference to type derived from T. (*t1).*f otherwise.

Now, I don't plan on using std::thread in this way, but I am flummoxed by this definition. Apparently, the only thing that happens is that the data member is accessed and the value ignored, which doesn't seem like it could have any observable side-effects at all, meaning (as far I can tell) it might as well be a no-op. (I might be missing something obvious...?)

At first, I thought this might be a misprint, and meant to say that the data member is accessed and then called (since it might be a callable object, even if it's not a function) but I tested it with the following code in GCC-4.7 and indeed there is no call:

#include <iostream>
#include <thread>

struct S 
{
    void f() {
        std::cout << "Calling f()" << std::endl;
    }

    struct {
        void operator()() {
            std::cout << "Calling g()" << std::endl;
        }
    } g;
};

int main(int, char**)
{
    S s;
    s.f(); // prints "Calling f()"
    s.g(); // prints "Calling g()"

    std::cout << "----" << std::endl;

    auto x = &S::f; // ptr-to-mem-func
    auto y = &S::g; // ptr-to-data-mem

    (s.*x)(); // prints "Calling f()"
    (s.*y)(); // prints "Calling g()"

    std::cout << "----" << std::endl;

    std::thread t(x, &s);
    t.join();
    // "Calling f()" printed by now
    std::thread u(y, &s);
    u.join();
    // "Calling g()" not printed

    return 0;
}

Is there any purpose to this definition which doesn't seem to accomplish anything? Why not instead make passing a "pointer-to-data-member-callable" act just like a "pointer-to-member-function", and make passing a "pointer-to-data-member-noncallable" an error? In fact, it seems like this would be the easiest way to implement it, since calling a "pointer-to-data-member-callable" has equivalent syntax to calling as a "pointer-to-member-function" in other contexts (unless there's something in the vagarities of template specialization and SFINAE rules which makes it difficult to treat them equivalently...?)

This is not something I need for actual code, but the fact that this definition exists leaves me suspecting that I am missing something fundamental, which worries me...can anyone enlighten me about this?

解决方案

This is because of the generic binding facility in terms of which the C++11 Standard defines not only how a thread is started, but also how std::bind and std::function work.

In fact, Paragraph 30.3.1.2/3 of the C++11 Standard specifies about the variadic constructor of class std::thread:

template <class F, class ...Args> explicit thread(F&& f, Args&&... args);

Effects: Constructs an object of type thread. The new thread of execution executes INVOKE (DECAY_- COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) with the calls to DECAY_COPY being evaluated in the constructing thread. Any return value from this invocation is ignored. [...]

Ignoring what DECAY_COPY does (it is not relevant to the question), this is how Paragraph 20.8.2 defines the INVOKE pseudo-function:

Define INVOKE (f, t1, t2, ..., tN) as follows:

— (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;

— ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;

— t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;

(*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 is not one of the types described in the previous item;

— f(t1, t2, ..., tN) in all other cases.

Now the question becomes:

Why does the C++11 Standard defines the INVOKE facility that way?

And the answer is here.

这篇关于std :: thread带有指向数据成员的指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆