Вызывать указатель на функцию шаблона с нулевым аргументом с переменным аргументом шаблона?
Вот фрагмент кода из поста в блоге Functional C++, описывающий, как можно реализовать обобщенную оценку функции.
Мой вопрос заключается в том, как вы можете объявить указатель на функцию шаблона f как R(C::*f)() без аргументов и при этом иметь возможность вызывать его с помощью Args…?
// functions, functors, lambdas, etc.
template<
class F, class... Args,
class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type,
class = typename std::enable_if<!std::is_member_object_pointer<F>::value>::type
>
auto eval(F&& f, Args&&... args) -> decltype(f(std::forward<Args>(args)...))
{
return f(std::forward<Args>(args)...);
}
// const member function
template<class R, class C, class... Args>
auto eval(R(C::*f)() const, const C& c, Args&&... args) -> R
{
return (c.*f)(std::forward<Args>(args)...);
}
template<class R, class C, class... Args>
auto eval(R(C::*f)() const, C& c, Args&&... args) -> R
{
return (c.*f)(std::forward<Args>(args)...);
}
// non-const member function
template<class R, class C, class... Args>
auto eval(R(C::*f)(), C& c, Args&&... args) -> R
{
return (c.*f)(std::forward<Args>(args)...);
}
// member object
template<class R, class C>
auto eval(R(C::*m), const C& c) -> const R&
{
return c.*m;
}
template<class R, class C>
auto eval(R(C::*m), C& c) -> R&
{
return c.*m;
}
struct Bloop
{
int a = 10;
int operator()(){return a;}
int operator()(int n){return a+n;}
int triple(){return a*3;}
};
int add_one(int n)
{
return n+1;
}
int main()
{
Bloop bloop;
// free function
std::cout << eval(add_one,0) << "\n";
// lambda function
std::cout << eval([](int n){return n+1;},1) << "\n";
// functor
std::cout << eval(bloop) << "\n";
std::cout << eval(bloop,4) << "\n";
// member function
std::cout << eval(&Bloop::triple,bloop) << "\n";
// member object
eval(&Bloop::a,bloop)++; // increment a by reference
std::cout << eval(&Bloop::a,bloop) << "\n";
return 0;
}
Например, когда я пытаюсь:
struct Bloop
{
int a = 10;
int operator()(){return a;}
int operator()(int n){return a+n;}
int triple(){return a*3;}
int foo(int n) {return n;}
};
template <typename R, typename C, typename... Args>
void eval (R(C::*func)(), C& c, Args... args) {
(c.*func)(args...);
}
int main()
{
Bloop bloop;
eval(&Bloop::foo, bloop, 5);
return 0;
}
Я получаю эту ошибку:
main.cpp: In function 'int main()':
main.cpp:27:31: error: no matching function for call to 'eval(int (Bloop::*)(int), Bloop&, int)'
eval(&Bloop::foo, bloop, 5);
^
main.cpp:27:31: note: candidate is:
main.cpp:19:6: note: template<class R, class C, class ... Args> void eval(R (C::*)(), C&, Args ...)
void eval (R(C::*func)(), C& c, Args... args) {
^
main.cpp:19:6: note: template argument deduction/substitution failed:
main.cpp:27:31: note: candidate expects 1 argument, 2 provided
eval(&Bloop::foo, bloop, 5);
^
И если я объявлю func
лайк R(C::*func)(int)
компилируется.
2 ответа
Код в статье блога неправильный (или, по крайней мере, неполный); это работает только для функций без аргументов. Вы могли бы написать eval
правильнее так:
template<class R, class C, class... T, class... Args>
auto eval(R(C::*f)(T...), C& c, Args&&... args) -> R
{
return (c.*f)(std::forward<Args>(args)...);
}
Обратите внимание T...
Пакет параметров для аргументов указателя на тип функции-члена. Это отдельный тип пакета из Args&&...
потому что две пачки могут быть выведены по-разному.
Этот код можно сделать одновременно более простым и более общим, избегая анализа типов указатель на член функции и указатель на элемент данных и просто принимая все, для чего вызовы четко определены:
#define RETURNS(...) \
-> decltype(__VA_ARGS__) { \
return (__VA_ARGS__); \
}
// Function object type
template<class F, class... Args>
auto eval(F&& f, Args&&... args)
RETURNS(std::forward<F>(f)(std::forward<Args>(args)...))
// pointer to member function, object reference
template<class PMF, class C, class... Args>
auto eval(PMF&& pmf, C&& c, Args&&... args)
RETURNS((std::forward<C>(c).*std::forward<PMF>(pmf))(std::forward<Args>(args)...))
// pointer to member data, object reference
template<class PMD, class C>
auto eval(PMD&& pmd, C&& c)
RETURNS(std::forward<C>(c).*std::forward<PMD>(pmd))
в то время как мы находимся на этом, мы можем также поддерживать пропущенные случаи указателей на члены с указателями объектов в дополнение к ссылкам на объекты для полноты, особенно учитывая, что пример кода требует их оценки eval(&Bloop::a,&bloop)++
:
// pointer to member data, object pointer
template<class PMD, class P>
auto eval(PMD&& pmd, P&& p)
RETURNS((*std::forward<P>(p)).*std::forward<PMD>(pmd))
// pointer to member function, object pointer
template<class PMF, class P, class... Args>
auto eval(PMF&& pmf, P&& p, Args&&... args)
RETURNS(((*std::forward<P>(p)).*std::forward<PMF>(pmf))(std::forward<Args>(args)...))
(Хорошо, может быть, "проще" было плохим выбором слова. "Более кратким" или "кратким", вероятно, было бы более точным.)