[英]How to write a member function with variadic parameters as template parameter
Is it possible to write template function in C++14 like below是否可以在 C++14 中编写模板函数,如下所示
Here is the sample https://godbolt.org/z/9gRk-t这是示例https://godbolt.org/z/9gRk-t
// pseudo code
#include <type_traits>
template <typename T, typename R, typename... Args>
decltype(auto) Call(T& obj, R(T::*mf)(Args...), Args&&... args)
{
return (obj.*mf)(std::forward<Args>(args)...);
}
So, for a test class所以,对于一个测试类
struct Test
{
int Func(){return 1;};
bool Func(bool){return true;}; // overload
void FuncInt(int){};
};
The template coudl work for the use case below (but it failed)模板 coudl 适用于下面的用例(但它失败了)
int main()
{
Test test;
// for overload case
auto a = Call<int()>(test, &Test::Func);
auto b = Call<bool(bool)>(test, &Test::Func, true);
// for non-overload case
Call(test, &Test::FuncInt, 1);
return 0;
}
Herer the erro info.这是错误信息。
#1 with x64 msvc v19.24
example.cpp
<source>(23): error C2672: 'Call': no matching overloaded function found
<source>(23): error C2770: invalid explicit template argument(s) for 'decltype(auto) Call(T &,R (__cdecl T::* )(Args...),Args &&...)'
<source>(5): note: see declaration of 'Call'
<source>(24): error C2672: 'Call': no matching overloaded function found
<source>(24): error C2770: invalid explicit template argument(s) for 'decltype(auto) Call(T &,R (__cdecl T::* )(Args...),Args &&...)'
<source>(5): note: see declaration of 'Call'
Compiler returned: 2
Issue with问题与
template <typename T, typename R, typename... Args>
decltype(auto) Call(T& obj, R(T::*mf)(Args...), Args&&... args)
is that Args
is deduced twice and should be identical.是
Args
导出两次并且应该是相同的。
There are several ways to fix that issue:有几种方法可以解决该问题:
Add extra template parameter:添加额外的模板参数:
template <typename T, typename R, typename... Args, typename ... Ts> decltype(auto) Call(T& obj, R(T::*mf)(Args...), Ts&&... args) { return (obj.*mf)(std::forward<Ts>(args)...); }
or make parameter non deducible (I use std::type_identity
from C++20 but can trivially be reimplemented in previous version):或使参数不可推导(我使用 C++20 中的
std::type_identity
但可以在以前的版本中轻松地重新实现):
template <typename T, typename R, typename... Args> decltype(auto) Call(T& obj, R(T::*mf)(Args...), std::type_identity_t<Args>... args) { return (obj.*mf)(std::forward<Args>(args)...); }
or change completely signature:或完全更改签名:
template <typename T, typename M, typename... Args> decltype(auto) Call(T& obj, M mf, Args&&... args) { return (obj.*mf)(std::forward<Args>(args)...); }
In your declaration of Call
:在您的
Call
声明中:
template <typename T, typename R, typename... Args>
decltype(auto) Call(T& obj, R(T::*mf)(Args...), Args&&... args);
the function template takes (or might try to deduce) two or more template arguments: the first is T
, the second is R
, and the rest are Args
.函数模板采用(或可能尝试推导出)两个或多个模板参数:第一个是
T
,第二个是R
,其余的是Args
。 So giving a single function type as the first template argument as in Call<int()>
and Call<bool(bool)>
is wrong.因此,在
Call<int()>
和Call<bool(bool)>
,将单个函数类型作为第一个模板参数是错误的。 The correct way to call it would be调用它的正确方法是
auto a = Call<Test, int>(test, &Test::Func);
auto b = Call<Test, bool, bool>(test, &Test::Func, true);
Another issue is that if you want the template arguments deduced, as in the non-overloaded case, since the Args
pack appears twice, it will only work if the lists deduced from the member function and from the trailing arguments are exactly the same:另一个问题是,如果您想要推导出模板参数,就像在非重载的情况下一样,由于
Args
包出现两次,只有从成员函数和尾随参数推导出的列表完全相同时,它才会起作用:
int n = 3;
Call(test, &Test::FuncInt, n); // error!
// Args... is deduced to `int` from `&Test::FuncInt`, but deduced to `int&`
// from `n` since it's an lvalue matching a forwarding reference parameter.
If you prefer the function type syntax, you could use the solution of @foo :如果您更喜欢函数类型语法,则可以使用@foo的解决方案:
template <typename FuncT, typename T, typename... Args>
constexpr decltype(auto) Call(T& obj, FuncT T::*mf, Args&&... args)
noexcept(noexcept((obj.*mf)(std::forward<Args>(args)...)))
{
return (obj.*mf)(std::forward<Args>(args)...);
}
// main() exactly as in question, including Call<int()> and Call<bool(bool)>.
FuncT T::*mf
is the syntax for declaring a pointer-to-member, which is often used to point to a data member, but also works to point to a function if the type FuncT
is a function type. FuncT T::*mf
是声明成员指针的语法,通常用于指向数据成员,但如果类型FuncT
是函数类型,也可用于指向函数。 (I've added the constexpr
and conditional exception specifier to make it more generic.) (我添加了
constexpr
和条件异常说明符以使其更通用。)
This also solves an issue with the original, which can't be used to invoke a member function which is const
or which has a ref-qualifier, since this creates a different function type:这也解决了原始函数的一个问题,它不能用于调用
const
或具有 ref 限定符的成员函数,因为这会创建不同的函数类型:
class Test2 {
public:
int get() const;
void set() &;
};
void driver_Test2() {
Test2 test;
// Error with original Call:
// Type of &Test2::get is "int (Test2::*)() const",
// which cannot match "R (Test2::*)(Args...)"
int a = Call(test, &Test2::get);
// Error with original Call:
// Type of &Test2::set is "void (Test2::*)(int) &",
// which cannot match "R (Test2::*)(Args...)"
Call(test, &Test2::set, 1);
}
But with the new Call
definition, driver_Test2
is fine, since any non-static member function can match FuncT T::*
.但是对于新的
Call
定义, driver_Test2
很好,因为任何非静态成员函数都可以匹配FuncT T::*
。 If we wanted to supply a template argument to the calls in driver_Test2
, maybe because the member functions are overloaded, that would look like Call<int() const>
and Call<void() &>
.如果我们想为
driver_Test2
的调用提供模板参数,可能是因为成员函数被重载了,它看起来像Call<int() const>
和Call<void() &>
。
I have figured it out.我已经想通了。 It is quietly like std::men_fn.
它安静地像 std::men_fn。
Here it is the code below and sample https://godbolt.org/z/NoWPV_这是下面的代码和示例https://godbolt.org/z/NoWPV_
#include <type_traits>
template<typename T>
struct Proxy
{
template<typename R, typename ...Args>
decltype(auto) Call(T& obj, R T::*mf, Args&&... args)
{
return (obj.*mf)(std::forward<Args>(args)...);
}
};
struct Test
{
int Func(){return 1;};
bool Func(bool){return true;}; // overload
void FuncInt(int){};
};
int main()
{
Test test;
Proxy<Test> proxy;
// for overload case
auto a = proxy.Call<int()>(test, &Test::Func);
auto b = proxy.Call<bool(bool)>(test, &Test::Func, true);
// for non-overload case
proxy.Call(test, &Test::FuncInt, 1);
return 0;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.