Подтвердить что ты не робот

Почему сбой указателя шаблона не работает для функции-указателя?

С 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, являясь точным соответствием?

4b9b3361

Ответ 1

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

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

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

Ответ 2

Тип &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);