0

C++ polymorphism functions taking void * and other pointer type as their arguments: is it considered ambiguous?

I am worried that since any pointer can be cast to void*, will the 2nd call of bar below executes void bar(void*) instead of my expected void bar(int*), in the program below?

I tested on my g++, and it runs as expected (i.e. int* won't be cast to void*). But can anyone comment/answer on/to this question in the respect of C++ language specifications?

foo.h:

class Foo {
public:
    void bar(void *);
    void bar(int *);
};

main.cpp:

...
struct A *p1;
int *p2;
Foo foo;
...
foo.bar(p1);
foo.bar(p2);

Furthermore, say, bar are now virtual polymorphism functions, taking void* argument as the 1st form, and base abstract class pointer argument as the 2nd form. Will a call with a derived class pointer as the argument execute the 1st form or the 2nd form? i.e. Will the derived class pointer been cast to its base abstract class pointer (and thus the 2nd form will be in action), or will it be cast to void * (and thus the 1st form will be in action) before calling bar()?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Robin Hsu
  • 4,164
  • 3
  • 20
  • 37
  • 3
    `int*` fits better than `void*`, so it'll be called in your example, but why are you even using `void*`? – Bartek Banachewicz Nov 13 '15 at 10:40
  • 1
    Write out your polymorphism case too and test to see what happens. If you're still interested in *why* it happens, then post the code that behaves the way you're asking about. – eerorika Nov 13 '15 at 10:46
  • 1
    @Bartek: `void *` is a common use to hide internal data structures, such as internal states. The caller just treat it as a black box, may pass it as an argument. – Robin Hsu Dec 02 '15 at 02:35
  • 1
    @RobinHsu I'd say using a template is more common in C++. – Bartek Banachewicz Dec 07 '15 at 14:50

1 Answers1

4

According to overload resolution rules (section Ranking of implicit conversion sequences), as the argument can be converted to either function's parameter type, the best viable function in this case would be the one whose implicit conversion is better.

For:

class Foo {
  public:
    void bar(void*);
    void bar(int*);
};

// ...

Foo foo;
int* p2;
foo.bar(p2);

The first is rank 3 (Conversion), while the second is rank 1 (Exact Match). As an exact match, which requires no conversion, is better than a conversion, it will call void bar(int*).

It gets more complex in your second case, however:

class Foo {
  public:
    virtual void bar(void*);
    virtual void bar(Foo*);
    virtual ~Foo() = default;
};

class FooTwo : public Foo {};

// ...

Foo foo;
FooTwo footwo;

foo.bar(&footwo);

As both are rank 3 (Conversion), this then follows conversion ranking rules. And as both conversions have the same conversion rank, this then goes to extended conversion ranking rules. Extended rule 2 states:

Conversion that converts pointer-to-derived to pointer-to-base is better than the conversion of pointer-to-derived to pointer-to-void, and conversion of pointer-to-base to void is better than pointer-to-derived to void.

Considering this, void bar(Foo*) is considered a better match than void bar(void*), meaning that it will be selected by foo.bar(&footwo);.

See here for an example of the latter.