std :: thread带有指向数据成员的指针

编程入门 行业动态 更新时间:2024-10-28 08:20:26
本文介绍了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 =stackoverflow/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带有指向数据成员的指针

本文发布于:2023-07-28 02:47:46,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1226594.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:指针   成员   数据   std   thread

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!