Скажем, у вас есть две структуры, 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);
Что здесь происходит?
Это сложно, но оказывается, что g ++ — это правильно.
Во-первых, тип выражения &Generic_B::p1
является void (Generic_A::*)()
, Компилятор использует Generic_B::
чтобы определить его имя поиска и находит члена Generic_A
, Тип выражения зависит от определения найденного члена, а не от типа, используемого в Квалифицированный-идентификатор.
Но это также законно иметь
void (Generic_B::*member)() = &Generic_B::p1;
так как есть неявное преобразование из void (Generic_A::*)()
в void (Generic_B::*)()
,
Всякий раз, когда шаблон функции используется в качестве вызова функции, компилятор проходит три основных этапа (или пытается):
Подставьте любые явные аргументы шаблона для параметров шаблона в объявлении функции.
Для каждого параметра функции, который все еще включает хотя бы один параметр шаблона, сравните соответствующий аргумент функции с этим параметром функции, чтобы (возможно) вывести эти параметры шаблона.
Подставьте выведенные параметры шаблона в объявление функции.
В этом случае у нас есть объявление шаблона функции
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
должен быть заменен пустым списком. Как вы уже обнаружили, вы можете получить эту работу, явно выполнив необходимый указатель на преобразование функции-члена самостоятельно, хотя в противном случае это допустимо как неявное преобразование.
Других решений пока нет …