нельзя передать указатель на метод в родительский класс через переменную функции — ошибка компилятора?

Скажем, у вас есть две структуры, Generic_A а также Generic_B, Generic_B происходит от Generic_A, Почему это когда Generic_B пытается получить доступ к методу в его родителе, Generic_A, он генерирует следующую ошибку:

test2.cpp: In function 'int main()':
test2.cpp:26: error: no matching function for call to 'case1(void (Generic_A::*)()'

Этот код, скомпилированный с использованием gcc версии 4.4.6, повторяет проблему:

#include <stdio.h>

struct Generic_A
{
void p1() { printf("%s\n", __PRETTY_FUNCTION__); };
};

struct Generic_B : public Generic_A
{
void p2() { printf("%s\n", __PRETTY_FUNCTION__); };
};

template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) ) {
printf("%s\n", __PRETTY_FUNCTION__);
}

template <class T>
void case2( void (T::*p)() ) {
printf("%s\n", __PRETTY_FUNCTION__);
}

main()
{
//generates error
case1<Generic_B>(&Generic_B::p1);

//compiles fine
case2<Generic_B>(&Generic_B::p1);
}

Единственное очевидное различие между двумя вызовами функций заключается в том, что case1() имеет параметр аргумента шаблона, и case2() не делает. Если они оба не позволят вам передать указатель функции на метод в родителе Generic_B (т.е. &Generic_B::p1)?

Кроме того, приведение указателя функции в case1 кажется, иногда разрешает ошибку:

case1<Generic_B>( (void (Generic_B::*)()) &Generic_B::p1);

Что здесь происходит?

2

Решение

Это сложно, но оказывается, что g ++ — это правильно.

Во-первых, тип выражения &Generic_B::p1 является void (Generic_A::*)(), Компилятор использует Generic_B:: чтобы определить его имя поиска и находит члена Generic_A, Тип выражения зависит от определения найденного члена, а не от типа, используемого в Квалифицированный-идентификатор.

Но это также законно иметь

void (Generic_B::*member)() = &Generic_B::p1;

так как есть неявное преобразование из void (Generic_A::*)() в void (Generic_B::*)(),

Всякий раз, когда шаблон функции используется в качестве вызова функции, компилятор проходит три основных этапа (или пытается):

  1. Подставьте любые явные аргументы шаблона для параметров шаблона в объявлении функции.

  2. Для каждого параметра функции, который все еще включает хотя бы один параметр шаблона, сравните соответствующий аргумент функции с этим параметром функции, чтобы (возможно) вывести эти параметры шаблона.

  3. Подставьте выведенные параметры шаблона в объявление функции.

В этом случае у нас есть объявление шаблона функции

template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) );

где параметры шаблона T а также ARGSи выражение вызова функции

case1<Generic_B>(&Generic_B::p1)

где явный аргумент шаблона Generic_B и аргумент функции &Generic_B::p1,

Итак, шаг 1, замените явный аргумент шаблона:

void case1( void (Generic_B::*p)(ARGS...) );

Шаг 2, сравните типы параметров и типы аргументов:

Тип параметра (P в стандартном разделе 14.8.2) void (Generic_B::*)(ARGS...), Тип аргумента (A) является void (Generic_A::*)(),

Стандарт C ++ (N3485) 14.8.2.1p4:

В общем, процесс дедукции пытается найти значения аргументов шаблона, которые сделают вывод A идентичный A (после типа A трансформируется как описано выше). Однако есть три случая, которые допускают разницу:

  • Если оригинал P является ссылочным типом, выведенным A (то есть тип, указанный в ссылке), может быть более квалифицированным по cv, чем преобразованный A,

  • Преобразованный A может быть другим указателем или указателем на тип члена, который может быть преобразован в выведенный A через преобразование квалификации (4.4).

  • Если P это класс и P имеет форму простой шаблон-идентификатор, тогда преобразованный A может быть производным классом выведенного A, Аналогично, если P это указатель на класс вида простой шаблон-идентификатор, преобразованный A может быть указателем на производный класс, на который указывает выведенный A,

Таким образом, вычитание типа допускает определенные неявные преобразования, включающие const / volatile и / или преобразования производных в базу, но неявные преобразования указателей на члены не рассматриваются.

в case1 Например, вывод типа не выполняется, и функция не совпадает.

К сожалению, нет никакого способа явно указать, что ваш пакет параметров шаблона ARGS должен быть заменен пустым списком. Как вы уже обнаружили, вы можете получить эту работу, явно выполнив необходимый указатель на преобразование функции-члена самостоятельно, хотя в противном случае это допустимо как неявное преобразование.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]