Проблемы с шаблонными методами, используемыми с шаблонным указателем функции на унаследованный метод

#c #templates #inheritance #c 98

#c #шаблоны #наследование #c 98

Вопрос:

Кажется, я не могу понять, что происходит с конкретным шаблонным методом.

Аналогичный шаблонный метод уже некоторое время находится в моей кодовой базе и используется таким же образом, как Bar::Bar(IFooamp;) , с той лишь разницей, что передаваемая функция наследуется.

У меня есть интерфейс IFoo , который определяет рассматриваемую шаблонную функцию.

 struct IFoo {
    template<class T>
    void doSomething(bool (T::*method)(int), T *instance) {
        std::cout << (instance->*method)(5) << "n";
    }
};
 

У меня есть класс Bar , который является дочерним элементом другого класса

 struct Parent {
    virtual bool setValue(int a) { return true; }
};
struct Bar : Parent { };
 

Когда я пытаюсь использовать IFoo::doSomething компилятор, он просто не видит соответствующую функцию

 int main() {
    Bar b;
    IFoo foo;
    foo.doSomething(amp;Bar::setValue, amp;b);
}
 

Я получаю следующее сообщение
об ошибке компилятора: нет соответствующей функции для вызова « IFoo::doSomething(bool (Parent::*)(int), Bar*)

Что меня действительно удивляет, так это то, что я не получаю предложения кандидата для шаблонной IFoo::doSomething функции.

Пожалуйста, только решения на C 98.

Комментарии:

1. Не выполняет сам вызов вместо виртуального метода?

2. Мне очень жаль, что вы все еще вынуждены использовать c 98

3. do это ключевое слово в C . Вы не можете назвать такой метод

4. Один метод do является частным и имеет параметры, отличные от общедоступной версии, которую я пытаюсь использовать.

5. Я отредактирую и переименую метод в моем примере, который не вызывается в моей кодовой базе

Ответ №1:

Как указано в ошибке, тип Bar::setValue на самом деле bool (Parent::*)(int) . Это мешает вычету параметра шаблона для doSomething , потому что у него есть два T s, которые должны быть одинаковыми. Вы можете помочь компилятору с выводом путем приведения this :

 int main() {
    Bar b;
    IFoo foo;
    foo.doSomething(amp;Bar::setValue, static_cast<Parent*>(amp;b));
}
 

Живая демонстрация

Спасибо JVApen за указание на то, что в качестве альтернативы вы можете сделать doSomething более общий, разрешив два разных типа и разрешить Callback преобразование:

 template<class T, class U>
void doSomething(bool (T::*method)(int), U *instance);
 

Комментарии:

1. В качестве альтернативы можно использовать 2 параметра шаблона и позволить обратному вызову неявно переводить строку в родительский указатель: godbolt.org/z/6adT8x

2. @JVApen спасибо за ссылку. Я думал то же самое, но, по общему признанию, был слишком ленив, чтобы убедиться, что это работает 😉

3. Однако я собирался добавить static_assert(std::is_base_of_v<T, U>) , что этого не существовало 22 года назад

4. @lancegerday «static_cast требует больших вычислительных затрат» это, должно быть, недоразумение. Почему вы так думаете?

5. Существует также шаблон dark magic «блокировщик дедукции»: template<class T> struct type_identity { /* stolen from C 20 */ typedef T type; }; template<class T> void doSomething(bool (T::*method)(int), typename type_identity<T>::type *instance); , в котором doSomething(amp;Bar::setValue, amp;b) работает, потому что только amp;Bar::setValue способствует выбору T = Parent . Затем передача становится неявной.