Почему вывод аргумента шаблона не выполняется для указателя на член-функцию?

С g++ 5.4 это

struct B {
    void f() {}
}; 

struct D : public B {
    void g() {}
};

template <class T>
void foo(void (T::*)(), void (T::*)())
{}

int main()
{
    foo(&D::f, &D::g);
}

не удается из-за "выведенных конфликтующих типов для параметра" T "(" B "и" D ")". Почему T не выводится как D, будучи точным соответствием?

2 ответа

Решение

В дополнение к отличной демонстрацииVTT. Я думаю, что стандартный текст находится по адресу [expr.unary.op] / 3, выделение мое:

Результатом унарного оператора & является указатель на его операнд. Операндом должно быть lvalue или квалифицированный идентификатор. Если операндом является квалифицированный идентификатор, называющий нестатический или вариантный член m некоторого класса C с типом T, результат имеет тип "указатель на член класса C типа T" и является предварительным значением, обозначающим C:: м

Квалифицированный идентификатор, который вы использовали D::f, но это называет функцию-член B (Я могу вызвать правила поиска, если хотите). Итак, тип класса C в приведенном выше абзаце, является B, Тип поэтому разрешает void ( B::* )(void),

Тип &D::f было бы void ( B::* )(void)

static_assert(::std::is_same<void ( B::* )(void), decltype(&D::f)>::value, "");
static_assert(::std::is_same<void ( D::* )(void), decltype(&D::f)>::value, ""); // error
static_assert(::std::is_same<void ( D::* )(void), decltype(&D::g)>::value, "");

Это объясняется тем, что в противном случае вы не сможете присвоить значение &D::f к переменной void ( B::* )(void) Тип без броска, хотя f является членом B или сравнить &D::f == &B::f,

В качестве обходного пути вы можете выполнить static_cast:

foo(static_cast<void (D::*)(void)>(&D::f), &D::g);
Другие вопросы по тегам