Reputation: 5209
I have variadic template class representing a thread with function and input arguments.
template<typename F> class my_thread;
template<typename Return, typename... Input>
class my_thread<Return(Input...)>
{
template<typename F>
my_thread(F&& f, Input&&... value) : /* mainly std::forward<Input>... to std::tuple<Input...> */ { }
}
Now, instancing that class is simple for global functions
int fnc(int, double);
my_thread<int(int, double)> thr(fnc, 42, 3.14);
It is (obviously) not that simple for function members of some class.
my_thread<int(int, double)> thr(&Foo::fnc, 42, 3.14); // won't work, unless Foo::fnc is static.
I know, that std::thread
, has some mechanics (probably partial specialization), which allows non-static member functions to be passed, if, before all arguments, pointer to instance of that class is passed (std::thread(&Foo::bar, Foo(), 42, 3.14); // OK
). I was not able to find out how to make that happen, so my my_thread
requires static member functions to be passed, and pointer to instance of that class has to be excplicit argument of that function.
struct Dog
{
void bark();
static void bark(Dog* dog)
{
dog->bark();
}
}
That is something I have to live with, but that is not problem.
Problem is instantiating my_thread
with that function. I wrote my_thread<int(Foo*, double)> thr(&Foo::bar, this, 3.14);
into Visual Studio 2015 and it complained about
error C2664: 'my_thread<int (Foo *, double)>::my_thread(my_thread<int (Foo *, double)> &&)': cannot convert argument 2 from 'Foo *const ' to 'Foo *&&'
I tried some magic with casting, but then, I found out, that passing &*this
instead of this
works.
I was happy to find out solution (or at least something that compiles and runs on Windows) and I decided to try it on linux with G++. I used same code (using &*this
), but G++ was angry at me, because
In member function 'void Foo::SomeFunction()': error: no matching function for call to ‘my_thread<int(Foo*, double)>::my_thread(int (*)(Foo*, double), Foo* const, double&)’
note: candidates are: my_thread(F&&, Input&&...)
note: template argument deduction/substitution failed:
note: cannot convert ‘this’ (type ‘Foo* const’) to type ‘Foo*&&’
I was quite surprised that I got pretty much same error as before. I edited back &*this
to just this
and I was able to compile and run that on G++ under linux.
My question is:
Who is right? G++ 4.9.2 or MVS2015? How do I solve it, so that my code can be run on both platforms?
Or, am I using bad approach to that? How is std::thread implemented, such that it knows, that when non-static member function is passed, it will require pointer to instance of that class as an argument?
EDIT
I am adding more code, so that my intention will be clearer.
template<typename F> struct my_thread; // std::function like syntax
template<typename Return, typename... Input>
struct my_thread<Return(Input...)>
{
struct thread_data
{
template<typename F>
thread_data(F&& _func, Input&&... _input) : func(std::forward<F>(_func)), input(std::forward<Input>(_input)...) { }
std::function<Return(Input...)> func;
std::tuple<Input...> input;
};
template<typename F>
my_thread(F&& _func, Input&&... _input) : data(std::forward<F>(_func), std::forward<Input>(_input)...) { }
thread_data data;
};
Upvotes: 1
Views: 3671
Reputation: 56956
Here:
template<typename F>
my_thread(F&& f, Input&&... value)
you are not deducing "universal references", but rvalue references to Input...
parameters (is this what you want?). Here, with Input = Foo*, double
, your constructor reads
template<typename F>
my_thread(F&& f, Foo*&& value1, double&& value2)
This explains the Foo*&&
part. Now, according to this question, the this
pointer is never a lvalue and therefore should have type Foo*
(and not Foo* const
), and should bind to Foo*&&
. This seems to be a MSVC bug.
[Regarding the GCC behavior when using &*this
, I don't know.]
Upvotes: 3
Reputation: 1783
Wouldn't it be that the const'ness of the pointer passed in is preventing the compiler from allowing the conversion? In your case, if you assume Foo &&, then you're receiving either a reference to a pointer or a pointer that is a temporary, but in either case, it's a pointer who's value you can actually modify. A pointer that is const in the sense that you have it, cannot change what it is pointing to, e.g., the destination of the pointer itself is stuck. There are two const's when it comes to pointers: one for the pointer itself, and one for what is pointed to. They're written "const" to the left and right of the "" in the variable declaration respectively.
Upvotes: 0