构造函数重载以接受任何函数
Constructor overloads to accept any function
我正在尝试创建一个 class 模板,其构造函数可以采用任何类型的函数作为参数,也就是说,它采用函数指针(可以是成员函数指针)和相应的函数参数。此外,应该有一个 static_assert
检查函数 return 类型(取自函数指针)是否与 class 模板参数类型匹配。因此,代码应如下所示:
template <class ReturnType>
struct Bar
{
template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType (* func)(ParamType ...), ParamType && ... args) :
package_(std::bind(func, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{
static_assert(std::is_same<ReturnType, RetType>::value,
"Type mismatch between class parameter type and constructor parameter type");
}
template <class RetType, class ObjType, class ... ParamType>
Bar<ReturnType>(RetType (ObjType::* func)(ParamType ...), ObjType * obj, ParamType && ... args) :
package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{
static_assert(std::is_same<ReturnType, RetType>::value,
"Type mismatch between class parameter type and constructor parameter type");
}
std::packaged_task<ReturnType()> package_;
std::function<void()> function_;
std::future<ReturnType> future_;
};
这个想法是针对这些情况编译代码,并允许 Bar::function_
被调用(通过函数调用运算符)而不会出错:
struct Foo
{
int foo(int i) {
return i;
}
int foo() {
return 1;
}
};
int foo(int i)
{
return i;
}
int foo()
{
return 1;
}
int main()
{
Foo f = Foo();
Bar<int> b1(&Foo::foo, &f, 1);
Bar<int> b2(&Foo::foo, &f);
Bar<int> b3(foo, 1);
Bar<int> b4(foo);
return 0;
}
不幸的是,我对模板元编程的经验几乎为零,尽管我在 SO 中有几个问题 运行,并尝试了几种解决问题的方法,例如使用更通用的方法到构造函数
template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType func, ParamType && ... args)
并将其与 type_traits
结合以确定 return 类型),我还没有找到一种方法来完成这项工作。我可以对允许此功能的构造函数进行哪些更改?
编辑:
max66的回答解决了我原来的问题,但是,出现了一个新问题,我在上一个问题中没有考虑到。我还希望能够将变量传递给构造函数,如下所示:
int main()
{
Foo f = Foo();
int i = 1;
Bar<int> b1(&Foo::foo, &f, i); // Error
Bar<int> b2(&Foo::foo, &f, 1); // Ok
Bar<int> b3(&Foo::foo, &f); // Ok
Bar<int> b4(foo, i); // Error
Bar<int> b5(foo, 1); // Ok
Bar<int> b6(foo); // Ok
return 0;
}
然而,实际上,在标有 Error
的情况下会出现编译器错误。我猜这是因为构造函数中的参数 func
使用 ParamType
来确定其类型(在 b1
的情况下与实际的 ParamType
s 不匹配和 b4
),但我不知道如何解决这个问题...
您可能想使用 std::invoke
。它为您处理成员函数指针和常规函数。
作为您可以做的事情的概述:
#include <functional>
#include <type_traits>
#include <utility>
template<typename F>
class Bar
{
F f_;
public:
template<typename TF>
Bar(TF && f)
: f_{ std::forward<TF>(f) }
{}
template<typename... Args>
decltype(auto) operator()(Args &&... args) {
return std::invoke(f_, std::forward<Args>(args)...);
}
};
template<typename F>
auto make_bar(F && f)
{
return Bar<std::decay_t<F>>{ std::forward<F>(f) };
}
可以这样使用:
auto b1 = make_bar(&f);
auto result = b1(myArg1, myArg2); // etc
auto b2 = make_bar(&Foo::fn);
auto result = b1(foo, arg1);
至少,我建议 Bar
将函数对象类型作为模板参数,这样您就不必使用 std::function
,但如果您确实想使用您拥有的确切调用语法,也可以使用 std::invoke
和 std::invoke_result
来完成。
抱歉,但是...如果您希望函数的 return 类型等于 class 的模板参数...您为什么不简单地强加它?
我的意思是...你可以用ReturnType
代替RetType
,如下
template <typename ReturnType>
struct Bar
{
template <typename ... ParamType>
Bar<ReturnType> (ReturnType (*func)(ParamType ...), ParamType && ... args)
: package_(std::bind(func, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
template <typename ObjType, typename ... ParamType>
Bar<ReturnType> (ReturnType (ObjType::* func)(ParamType ...),
ObjType * obj, ParamType && ... args)
: package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
-- 编辑--
解决第二个问题,如果你对移动参数不感兴趣,可以扔掉std::forward
和&&
,直接写
template <typename ReturnType>
struct Bar
{
template <typename ... ParamType>
Bar<ReturnType> (ReturnType (*func)(ParamType ...),
ParamType const & ... args)
: package_(std::bind(func, args...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
template <typename ObjType, typename ... ParamType>
Bar<ReturnType> (ReturnType (ObjType::* func)(ParamType ...),
ObjType * obj, ParamType const & ... args)
: package_(std::bind(func, obj, args...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
我正在尝试创建一个 class 模板,其构造函数可以采用任何类型的函数作为参数,也就是说,它采用函数指针(可以是成员函数指针)和相应的函数参数。此外,应该有一个 static_assert
检查函数 return 类型(取自函数指针)是否与 class 模板参数类型匹配。因此,代码应如下所示:
template <class ReturnType>
struct Bar
{
template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType (* func)(ParamType ...), ParamType && ... args) :
package_(std::bind(func, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{
static_assert(std::is_same<ReturnType, RetType>::value,
"Type mismatch between class parameter type and constructor parameter type");
}
template <class RetType, class ObjType, class ... ParamType>
Bar<ReturnType>(RetType (ObjType::* func)(ParamType ...), ObjType * obj, ParamType && ... args) :
package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{
static_assert(std::is_same<ReturnType, RetType>::value,
"Type mismatch between class parameter type and constructor parameter type");
}
std::packaged_task<ReturnType()> package_;
std::function<void()> function_;
std::future<ReturnType> future_;
};
这个想法是针对这些情况编译代码,并允许 Bar::function_
被调用(通过函数调用运算符)而不会出错:
struct Foo
{
int foo(int i) {
return i;
}
int foo() {
return 1;
}
};
int foo(int i)
{
return i;
}
int foo()
{
return 1;
}
int main()
{
Foo f = Foo();
Bar<int> b1(&Foo::foo, &f, 1);
Bar<int> b2(&Foo::foo, &f);
Bar<int> b3(foo, 1);
Bar<int> b4(foo);
return 0;
}
不幸的是,我对模板元编程的经验几乎为零,尽管我在 SO 中有几个问题 运行,并尝试了几种解决问题的方法,例如使用更通用的方法到构造函数
template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType func, ParamType && ... args)
并将其与 type_traits
结合以确定 return 类型),我还没有找到一种方法来完成这项工作。我可以对允许此功能的构造函数进行哪些更改?
编辑:
max66的回答解决了我原来的问题,但是,出现了一个新问题,我在上一个问题中没有考虑到。我还希望能够将变量传递给构造函数,如下所示:
int main()
{
Foo f = Foo();
int i = 1;
Bar<int> b1(&Foo::foo, &f, i); // Error
Bar<int> b2(&Foo::foo, &f, 1); // Ok
Bar<int> b3(&Foo::foo, &f); // Ok
Bar<int> b4(foo, i); // Error
Bar<int> b5(foo, 1); // Ok
Bar<int> b6(foo); // Ok
return 0;
}
然而,实际上,在标有 Error
的情况下会出现编译器错误。我猜这是因为构造函数中的参数 func
使用 ParamType
来确定其类型(在 b1
的情况下与实际的 ParamType
s 不匹配和 b4
),但我不知道如何解决这个问题...
您可能想使用 std::invoke
。它为您处理成员函数指针和常规函数。
作为您可以做的事情的概述:
#include <functional>
#include <type_traits>
#include <utility>
template<typename F>
class Bar
{
F f_;
public:
template<typename TF>
Bar(TF && f)
: f_{ std::forward<TF>(f) }
{}
template<typename... Args>
decltype(auto) operator()(Args &&... args) {
return std::invoke(f_, std::forward<Args>(args)...);
}
};
template<typename F>
auto make_bar(F && f)
{
return Bar<std::decay_t<F>>{ std::forward<F>(f) };
}
可以这样使用:
auto b1 = make_bar(&f);
auto result = b1(myArg1, myArg2); // etc
auto b2 = make_bar(&Foo::fn);
auto result = b1(foo, arg1);
至少,我建议 Bar
将函数对象类型作为模板参数,这样您就不必使用 std::function
,但如果您确实想使用您拥有的确切调用语法,也可以使用 std::invoke
和 std::invoke_result
来完成。
抱歉,但是...如果您希望函数的 return 类型等于 class 的模板参数...您为什么不简单地强加它?
我的意思是...你可以用ReturnType
代替RetType
,如下
template <typename ReturnType>
struct Bar
{
template <typename ... ParamType>
Bar<ReturnType> (ReturnType (*func)(ParamType ...), ParamType && ... args)
: package_(std::bind(func, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
template <typename ObjType, typename ... ParamType>
Bar<ReturnType> (ReturnType (ObjType::* func)(ParamType ...),
ObjType * obj, ParamType && ... args)
: package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
-- 编辑--
解决第二个问题,如果你对移动参数不感兴趣,可以扔掉std::forward
和&&
,直接写
template <typename ReturnType>
struct Bar
{
template <typename ... ParamType>
Bar<ReturnType> (ReturnType (*func)(ParamType ...),
ParamType const & ... args)
: package_(std::bind(func, args...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
template <typename ObjType, typename ... ParamType>
Bar<ReturnType> (ReturnType (ObjType::* func)(ParamType ...),
ObjType * obj, ParamType const & ... args)
: package_(std::bind(func, obj, args...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }