【发布时间】:2017-06-04 19:16:22
【问题描述】:
我正在尝试创建一个类模板,其构造函数可以将任何类型的函数作为参数,也就是说,它需要一个函数指针(可以是成员函数指针)和相应的函数参数。此外,应该有一个static_assert 来检查函数返回类型(取自函数指针)是否与类模板参数类型匹配。因此,代码应如下所示:
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 结合以确定返回类型),我还没有找到一种方法来完成这项工作。我可以对允许此功能的构造函数进行哪些更改?
编辑:
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 和b4 的情况下与实际的ParamTypes 不匹配),但我不知道怎么解决...
【问题讨论】:
-
如果你使用 C++17,你可以使用
std::invoke让你的生活更轻松。如果你使用 C++14,一些库实现了std::invoke的等价物(例如 Boost Hana 有hana::apply) -
您的示例是重载函数,因此您“不能”在不选择特定重载的情况下仅获取它们的地址。尽管如此,你可以让你的构造函数接受第一个参数并使用一个 trait 来确保它是一个函数类型,然后可变参数转发引用来获取参数的其余部分,最后使用类似
std::invoke的服务 -
我确实在使用 C++17 特性,但我对 std::invoke 了解不多。我将如何实现我打算使用它的功能?
标签: c++ templates c++17 constructor-overloading