Почему временные функции-члены не привязываются к нужному типу? - PullRequest
6 голосов
/ 17 сентября 2009

Предположим, что у нас есть следующие базовые и производные классы:

#include <string>
#include <iostream>

class Car {
public:
    void Drive() { std::cout << "Baby, can I drive your car?" << std::endl; }
};

class Porsche : public Car {
};

.. а также следующая функция шаблона:

template <typename T, typename V>
void Function(void (T::*m1)(void), void (V::*m2)(void)) {
    std::cout << (m1 == m2) << std::endl;
}

Почему эта компиляция с использованием GCC:

int main(int argc, char** argv) {
    void (Porsche::*ptr)(void) = &Porsche::Drive;
    Function(ptr, ptr);
    return 0;
}

... но не это?

int main(int argc, char** argv) {
    void (Porsche::*ptr)(void) = &Porsche::Drive;
    Function(&Porsche::Drive, ptr);
    return 0;
}

Ответы [ 2 ]

7 голосов
/ 17 сентября 2009
int main(int argc, char** argv) {
    void (Porsche::*ptr)(void) = &Porsche::Drive;
    Function(&Porsche::Drive, ptr);
    return 0;
}

ptr имеет тип void (Porsche::*)(), но &Porsche::Drive имеет тип void (Car::*)() (поскольку элемент находится в Car, а не в Porsche). Таким образом, вызываемая функция сравнивает эти два указателя на член с этими типами, а стандарт говорит:

Кроме того, можно сравнивать указатели на члены или указатель на член и константу нулевого указателя. Преобразования указателей в члены (4.11) и квалификационные преобразования (4.4) выполняются, чтобы привести их к общему типу. Если один операнд является константой нулевого указателя, общий тип является типом другого операнда. В противном случае общий тип - это указатель на тип члена, аналогичный (4.4) типу одного из операндов, с сигнатурой cv-квалификации (4.4), которая является объединением сигнатур cv-квалификации типов операндов.

4.11 описывает неявное стандартное преобразование из void (Base::*)() в void (Derived::*)(). Таким образом, сравнение нашло бы общий тип void (Porsche::*)(). Для объекта типа Porsche оба указателя-члена будут ссылаться на одну и ту же функцию (то есть Car::Drive), поэтому сравнение даст значение true. Веб-компилятор comeau следует этой интерпретации и компилирует ваш код.

1 голос
/ 17 сентября 2009

В g ++ 4.0.1 примеры, которые вы предоставляете, прекрасно компилируются, но возникают проблемы, связанные со сравнением указателей на функции-члены разных типов (даже если сравниваемый элемент является виртуальным методом.

struct base
{
   void f() {}
   virtual void g() {}
};
struct derived : public base
{
   void f() {}
   virtual void g() {}
};
template <typename T, typename U>
bool cmp( void (T::*lhs)(), void (U::*rhs)() ) {
   return lhs == rhs;
}
int main()
{
   void (base::*bp)() = &base::f;
   void (base::*bvp)() = &base::g;

   cmp( bp, &base::f );  // compiles
   cmp( bvp, &base::g ); // compiles

   void (derived::*dp)() = &derived::f;
   void (derived::*dvp)() = &derived::g;

   cmp( dp, &derived::f );  // compiles
   cmp( dvp, &derived::g ); // compiles

   cmp( bp, dp );   // fails to compile
   cmp( bvp, dvp ); // fails to compile
}

Теперь я протестировал то же самое с онлайн-компилятором comeau, и весь код компилируется нормально. Я не могу сразу сказать вам, что такое стандартное поведение. Мне придется некоторое время просматривать стандарт.

РЕДАКТИРОВАТЬ: я думаю, нет, Литб уже сделал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...