Ошибка вызова шаблонной функции-указателя на член с типом возвращаемого значения

#c #templates #variadic-templates #member-function-pointers #template-argument-deduction

#c #шаблоны #переменные-шаблоны #указатель на член #шаблон-аргумент-вычитание

Вопрос:

 template<typename T, typename F, typename ...Args>
auto f(F h, Argsamp;amp;... args) -> decltype(h(args...)) {
    T* t = new T(); // Don't worry, my actual code doesn't do this
    return (t->h)(args...);
}

struct G {
    int g() {
        return 5;
    }
};

int main() {
    int k = f<G>(amp;G::g);
}
  

Компилятор Microsoft говорит error C2672: 'f': no matching overloaded function found и error C2893: Failed to specialize function template 'unknown-type f(F,Args amp;amp;...)' .

Компилятор Clang сообщает note: candidate template ignored: substitution failure [with T = G, F = int (G::*)(), Args = <>]: called object type 'int (G::*)()' is not a function or function pointer и error: no matching function for call to 'f' .

Я почти уверен, что int (G::*)() это указатель на функцию … ? Что я упускаю? (Все это отлично работало до того, как я добавил возвращаемый тип.)

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

1. Компилятор рассказывает вам все. Вы не можете вызвать h(...) , потому что h она не вызывается сама по себе, когда h является указателем на член. Если вы используете C 14, проще всего было бы просто опустить тип возвращаемого значения traliling.

2. Я не понимаю, как это сработало. Что вы подразумеваете под тем, что h не может быть вызван сам по себе?

3. О, подождите, я понимаю, вы не можете вызвать просто h(), вам нужно t-> h.

4. Вы можете использовать что-то вроде decltype((std::declval<T>().*h)(args...))

Ответ №1:

Я почти уверен, что int (G::*)() это указатель на функцию … ? Что я упускаю?

Не совсем точно: int (G::*)() является указателем на нестатический метод. Это не совсем одно и то же, и для ее вызова требуется немного другой синтаксис.

Итак, вместо

 return (t->h)(args...);
  

вы должны добавить * и вызвать h() следующим образом

 return (t->*h)(args...);
// ........^  add this *
  

decltype() Также неверно. Если вы можете использовать хотя бы C 14, вы можете избежать этого и просто использовать auto в качестве возвращаемого типа

 template <typename T, typename F, typename ...Args>
auto f (F h, Argsamp;amp;... args) {
    T* t = new T(); 
    return (t->*h)(args...);
}
  

в противном случае, если вам необходимо использовать C 11, вы можете включить <utility> и использовать std::declval() следующим образом

 template <typename T, typename F, typename ...Args>
auto f(F h, Argsamp;amp;... args) -> decltype((std::declval<T*>()->*h)(args...)) { 
    T* t = new T(); // .................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    return (t->*h)(args...);
}
  

Но есть другой способ написать вашу f() функцию: вывести возвращаемый тип (таким образом, избегая auto , decltype() и std::declval() ) и аргументы h()

Вы можете написать f() следующим образом

 template<typename R, typename T, typename ... As1, typename ... As2>
R f(R(T::*h)(As1...), As2 amp;amp; ... args) {
    T* t = new T();
    return (t->*h)(args...);
}
  

и вы избегаете явного G типа, вызывающего ее

 int k = f(amp;G::g);
// .....^^^^^^^^   no more explicit <G> needed
  

поскольку T тип шаблона выводится из аргумента amp;G::g .