0

Функции полиморфизма C++, принимающие void * и другие типы указателей как их аргументы: он считается неоднозначным?Функция полиморфизма C++, принимающая void * и другой тип указателя в качестве аргумента: считается ли она неоднозначной?

Я беспокоюсь, что поскольку любой указатель может быть приведен к void*, будет ли второй вызов бара ниже выполняет void bar(void*) вместо моего ожидается void bar(int*), в следующей программе?

Я тестировал на своем g ++, и он работает как ожидалось (т. Е. Int * не будет передан в void *). Но может ли кто-нибудь прокомментировать/ответить на этот вопрос в отношении спецификаций языка C++?

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); 

Кроме того, скажем, bar теперь виртуальные функции полиморфизм, принимая void* аргумент как 1-й формы, и указатель базового абстрактного класса аргумент как вторая форма. Будет ли вызов с указателем производного класса в качестве аргумента выполнить 1-й или 2-й формы? То есть, будет ли указатель производного класса передан его базовому абстрактному указателю класса (и, следовательно, будет действовать 2-я форма), или будет ли он передан в void * (и, таким образом, первая форма будет действовать) перед вызовом bar()?

+3

'int *' подходит лучше, чем 'void *', поэтому он будет вызываться в вашем примере, но почему вы даже используете 'void *'? –

+1

Запишите свой полиморфизм и проверьте, что произойдет. Если вас все еще интересует *, почему это происходит, тогда опубликуйте код, который ведет себя так, как вы просите. – user2079303

+0

@Bartek: 'void *' является общим для скрытия внутренних структур данных, таких как внутренние состояния. Вызывающий может рассматривать его как черный ящик, может передать его в качестве аргумента. –

ответ

3

Согласно overload resolution rules (section Ranking of implicit conversion sequences), поскольку аргумент может быть преобразован в тип параметра любой функции, лучшей жизнеспособной функцией в этом случае будет тот, чье неявное преобразование лучше.

Для:

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

// ... 

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

Первый из них ранга 3 (преобразования), а второй ранга 1 (Четк.совп). Как точное совпадение, которое не требует преобразования, лучше, чем преобразование, оно будет вызывать void bar(int*).

Это становится все более сложным в вашем втором случае, однако:

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); 

Как оба ранга 3 (Конверсия), это следует тогда правила преобразования рейтинга. И поскольку оба преобразования имеют одинаковый ранг конверсии, это затем переходит к расширенным правилам ранжирования конверсий. Расширенные правило 2 гласит:

преобразование, который преобразует указатель на полученные в-к-основание указателя лучше, чем преобразования указателя на производном, чтобы указатель на пустоту, и преобразование указателя-to base to void лучше, чем указатель-to-производный для void.

Учитывая это, void bar(Foo*) считается лучше, чем матч void bar(void*), а это означает, что он будет выбран foo.bar(&footwo);.

См. here для примера последнего.