16

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.

Ram
  • 3,045
  • 3
  • 27
  • 42
  • I do. This was just an example to demonstrate the problem. – Ram Jan 11 '13 at 10:47
  • AFAIK `std::thread` constructor only supports static function pointers or appropriate functor classes as parameter. To call class member methods, you'll need a wrapper class that keeps the actual class instance along with the function pointer to call. – πάντα ῥεῖ Jan 11 '13 at 10:51
  • makulik, thanks for your reply. What I want to know is Is this a restriction imposed by the C++ standard or the compiler vendors are yet to get around this problem. – Ram Jan 11 '13 at 10:55
  • @g-makulik, no, `std::thread` supports calling member functions directly. The problem is that `&Complex::display` is ambiguous, not a problem with `std::thread` – Jonathan Wakely Jan 13 '13 at 18:50

5 Answers5

23

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);
Cadoiz
  • 1,446
  • 21
  • 31
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Thanks! I have also found that the order of functions declared in .h is important: it should go from less arguments to overloaded functions with more arguments. – Mickey Tin Jan 24 '19 at 23:37
  • @MickeyTin in general that's not true, the order doesn't matter. It certainly doesn't matter in this instance. – Jonathan Wakely Jan 24 '19 at 23:48
  • That what I observed, I wasn't able to compile until I changed the order. The compiler kept complaining about wrong arguments number until I switched the functions. I'm building Android NDK project. – Mickey Tin Jan 24 '19 at 23:54
  • You saved my day. However, I think the `thread` class should be able to handle overloaded member functions since the compiler has all the information required at build time. There is no possible reason than being just "inconsiderate". I'll simply avoid overloading for multuthreading candidates. – Jake 'Alquimista' LEE Feb 09 '22 at 09:24
  • @Jake'Alquimista'LEE but `std::thread` is just a library type, that you could write yourself in pure C++. The compiler doesn't know that `std::thread{&Complex::display, &c1}` is going to call that member with no arguments. It's opaque to the compiler, and no different to `some_arbitrary_function(&Complex::display, &c1)`. There is simply no way in the C++ language to pass an overloaded function as a function argument, so `std::thread` can't do it, and your own functions can't do it. – Jonathan Wakely Feb 09 '22 at 12:44
6

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;
}
billz
  • 44,644
  • 9
  • 83
  • 100
  • That is a nice workaround. But I want to know why there is no support for overloaded member functions. Is it not possible to provide such support or they are yet to provide the same ? – Ram Jan 11 '13 at 10:51
  • WTH do you exactly mean with _'overloaded member function'_?? I can't see any 'overloading' demonstrated in your code. – πάντα ῥεῖ Jan 11 '13 at 10:56
  • void display(), void display(unsigned nTimes) are two functions on Complex class that have the same name but different parameters. – Ram Jan 11 '13 at 10:57
  • @g-makulik he has display which is overloaded but using lambda and std::thread has no issue at all – billz Jan 11 '13 at 10:57
  • Your first sentence is wrong now. The syntax is perfectly fine if the function is not overloaded and that is basically what he is asking. See here: http://stackoverflow.com/questions/10673585/start-thread-with-member-function/10673671#10673671 – Stephan Dollberg Jan 11 '13 at 11:06
  • No, it shows the standard 'calling thread with member function' example which you claimed in your - now edited out sentence - first sentence doesn't work. It's the same as the op is trying to use. – Stephan Dollberg Jan 11 '13 at 11:29
6

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.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
4

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);
bluescarni
  • 3,937
  • 1
  • 22
  • 33
0

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;
} 
pacs
  • 1