EDIT: RETRACTION
Kerrek is correct here: I erroneously assumed that the std::thread
constructor and std::bind
were by design identical interfaces. However, the automatic conversion of arguments from reference_wrapper<A>
to A&
is specified for only std::bind
in [func.bind.bind]/10:
The values of the bound arguments v1, v2, ..., vN
and their corresponding types V1, V2, ..., VN
depend on the types TiD
derived from the call to bind
and the cv-qualifiers cv of the call wrapper g
as follows:
- if
TiD
is reference_wrapper<T>
, the argument is tid.get()
and its type Vi
is T&
;
- ...
So this particular use of reference_wrapper<A>
is not supported by std::thread
, but is supported by std::bind
. The fact that std::thread
behaves identically to std::bind
in this instance in other/older compilers is the bug, not the behavior of 4.8 line GCC releases.
I'll leave the incorrect answer here with this explanation in hopes that others won't make this same mistake in the future.
Short (but INCORRECT) answer
This is apparently a bug in the standard library included with GCC 4.8. The code is correctly compiled by:
Long (and also INCORRECT) answer:
The effects of the std::thread
constructor
template <class F, class ...Args>
explicit thread(F&& f, Args&&... args);
are detailed in C++11 30.3.1.2 [thread.thread.constr]/4:
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.
DECAY_COPY
is described in 30.2.6 [thread.decaycopy]/1:
In several places in this Clause the operation DECAY_COPY(x)
is used. All such uses mean call the function decay_copy(x)
and use the result, where decay_copy
is defined as follows:
template <class T> typename decay<T>::type decay_copy(T&& v)
{ return std::forward<T>(v); }
In the invocation in the OP std::thread t1(&A::foo, std::ref(a), 100);
all three arguments are rvalues that DECAY_COPY
will replicate into objects in the new thread's environment before the invocation, whose effect is described in 20.8.2 [func.require]/1:
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;
- ...
For the code in the OP, f
is a pointer to member function of class A
with value &A::foo
, t1
is an lvalue reference_wrapper<A>
whose stored reference refers to a
, and t2
is an int
with value 100
. The second bullet of 20.8.2/1 applies. Since t1
is a reference_wrapper
, *t1
evaluates to the stored reference (per 20.8.3.3/1) and the invocation in the new thread is effectively
(a.*&A::foo)(100);
So yes, the standard describes the behavior of the OP exactly as expected.
EDIT: Oddly, GCC 4.8 correctly compiles the very similar example:
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();
}