简体   繁体   中英

Calling overloaded member functions using std::thread

Is it possible to have overloads for functions that we need to span using threads ?

I have a simple class called Complex.

class Complex
{
public:
    Complex():realPart_(0), imagPart_(0){}

    Complex(double rp, double ip) : realPart_(rp), imagPart_(ip) {}

    double & real() { return realPart_;}
    double & imag() { return imagPart_;}

    const double & real() const { return realPart_;}
    const double & imag() const { return imagPart_;}

    double square() const {return realPart_*realPart_ - imagPart_*imagPart_;}

    void display() const
    {
        std::cout << "Square of the Complex number (" << realPart_ << ") + i (" << imagPart_ << " ) is " << square() << std::endl;  
    }

    void display(unsigned nTimes) const {while(nTimes-- > 0)display();}

private:

    double realPart_;
    double imagPart_;

};

void Test3()
{
    Complex c1(1, 0.74), c2(2, 0.35);

    std::thread sqCalc1(&Complex::display, &c1);
    std::thread sqCalc2(&Complex::display, &c2);

    sqCalc1.join();
    sqCalc2.join();
}

I get errors when I build this code.

error C2661: 'std::thread::thread' : no overloaded function takes 2 arguments

If there is no overloaded display function that takes an unsigned then the code I have shown works fine.

The problem is nothing to do with std::thread (the error is misleading), as can be shown by rearranging the code:

auto memfunc = &Complex::display;
std::thread sqCalc1(memfunc, &c1);
std::thread sqCalc2(memfunc, &c2);

The error will be on the first line now, because as other answers have said, the expression &Complex::display refers to an overloaded function and the compiler doesn't know which one you mean.

You can select the desired overload by telling the compiler the type of the function you are trying to call, with a cast or like this:

void (Complex::*memfunc)() const = &Complex::display;
std::thread sqCalc1(memfunc, &c1);
std::thread sqCalc2(memfunc, &c2);

Now you've explicitly requested the display overload that returns void and takes no arguments.

If your compiler supports C++11 alias declarations you can make that easier to read:

using memfunc_type = void (Complex::*)() const;
memfunc_type memfunc = &Complex::display;
std::thread sqCalc1(memfunc, &c1);
std::thread sqCalc2(memfunc, &c2);

lambda can be used here, you could call any object function and pass arguments in as well:

int main()
{
    Complex c1(1, 0.74), c2(2, 0.35);

    std::thread sqCalc1([=]{c1.display();});
    std::thread sqCalc2([=]{c2.display(3);});

    sqCalc1.join();
    sqCalc2.join();
    return 0;
}

Contrary to some comments on the question, this is not a problem of C++11 restricting the ctor argument list, nor is it a compiler problem. std::thread constructor may take a pointer-to-member-function, followed by the object reference/pointer on wich the member function shall be called, followed by member function arguments (if there are any).

The problem at hand is just a disambiguation issue, just by seeing &Complex::display , the compiler has no chance to knwo which of the overloads you mean, because while deducting the template arguments it does not know that inside the constructor the function pointer will be called with the other arguments and that therefore only the unary or 0-ary member function makes sense.

2 Possible solutions have been shown by bluescarni and billz:

  1. Pass a lambda to the thread's constructor, because inside the lambda overload resolution can determine wich display function gets called.
  2. Cast the member function pointer to the correct function pointer type, so the compiler knows which one to pick for template argument deduction.

A third possibility would be to explicitly specify the template parameter of the function pointer, but sadly it is not possible to explicitly instantiate templated constructors:

std::thread sqCalc1<Complex::*()const>(&Complex::display, &c1); //doesn't work

However, this would not make much difference to the explicit cast and argument deduction. and I prefer using lambdas anyways, in all cases, even if there is no such ambiguity, just because you can put a breakpoint right before the function call.

Maybe typedeffing and casting could help?

typedef void (Complex::*display_0)() const;
typedef void (Complex::*display_1)(unsigned) const;

std::thread sqCalc1(display_0(&Complex::display), &c1);
std::thread sqCalc2(display_0(&Complex::display), &c2);

Although it is not about overriding member function, the only way to thank @billz for the lambda solution is to contribute with "my" code, that is the simplest case of the thread-call problem, again, solved with lambdas as proposed above.

#include <thread>
void f(int a){}
void f(int a, int b){}

int main()
{
   std::thread t([=]{ f(2); });
   t.join();

   return 0;
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM