大多数答案都集中在基本回答这个问题上:你能用这些类型的值调用给定的函数对象吗?这与匹配签名不同,因为它允许您说您不想要的许多隐式转换。为了获得更严格的匹配,我们必须做一堆 TMP。首先,这个答案:Call function with part of variadic arguments展示了如何获取参数的确切类型和可调用的返回类型。此处转载的代码:
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
{
using result_type = ReturnType;
using arg_tuple = std::tuple<Args...>;
static constexpr auto arity = sizeof...(Args);
};
template <typename R, typename ... Args>
struct function_traits<R(&)(Args...)>
{
using result_type = R;
using arg_tuple = std::tuple<Args...>;
static constexpr auto arity = sizeof...(Args);
};
完成后,您现在可以在代码中放置一系列静态断言:
struct A {
using Signature = void(int, double);
template <typename Callable>
void Register(Callable &&callable) {
using ft = function_traits<Callable>;
static_assert(std::is_same<int,
std::decay_t<std::tuple_element_t<0, typename ft::arg_tuple>>>::value, "");
static_assert(std::is_same<double,
std::decay_t<std::tuple_element_t<1, typename ft::arg_tuple>>>::value, "");
static_assert(std::is_same<void,
std::decay_t<typename ft::result_type>>::value, "");
callback = callable;
}
std::function<Signature> callback;
};
由于您是按值传递,因此这基本上就是您所需要的。如果您通过引用传递,我会在您使用其他答案之一的地方添加一个额外的静态断言;大概是宋元瑶的回答。这将处理例如基本类型相同但 const 限定方向错误的情况。
您当然可以在 type 上使这一切通用Signature
,而不是做我所做的(简单地重复静态断言中的类型)。这会更好,但它会给已经很重要的答案添加更复杂的 TMP;如果您觉得您将使用它与许多不同Signature
的 s 或者它经常更改,那么可能也值得添加该代码。
这是一个活生生的例子:http ://coliru.stacked-crooked.com/a/cee084dce9e8dc09 。特别是,我的例子:
void foo(int, double) {}
void foo2(double, double) {}
int main()
{
A a;
// compiles
a.Register([] (int, double) {});
// doesn't
//a.Register([] (int, double) { return true; });
// works
a.Register(foo);
// doesn't
//a.Register(foo2);
}