Zereges
Zereges

Reputation: 5209

C++11: cannot convert argument from 'T *const' to 'T *&&'

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

Answers (2)

Alexandre C.
Alexandre C.

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

Adam Miller
Adam Miller

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

Related Questions