Variadic 模板方法和 std::function - 编译错误
Variadic template method and std::function - compilation error
我确定错误非常简单和愚蠢,但我看不到。这是代码:
#include <future>
template <typename ResultType>
class Foo
{
public:
template <typename ...Args>
void exec(const std::function<ResultType(Args...)>& task, Args&&... args) {}
};
int main()
{
Foo<void>().exec([](){});
return 0;
}
这是错误:
'void CAsyncTask::exec(const std::function
&,Args &&...)' : could not deduce template argument for 'const
std::function &' with [
ResultType=void ]
Foo<void>().exec<void>([](){})
也不起作用(我更希望不必手动指定 Args
类型)。
关于建议答案的更新:以下代码确实有效。
CAsyncTask<void>().exec(std::function<void ()>([](){}));
但是这个问题真的没有解决方法吗?我能否以某种方式扩展我的模板以推断出 lambda 参数?
正如 Pradhan 提到的,std::function
的确切类型无法从 lambda 中推断出来。您可以显式转换它来解决问题:
Foo<void>().exec(std::function<void()>([](){}));
或者,您可以简单地为函数使用另一个类型名称,而无需创建 std::function
:
template <typename Callable, typename ...Args>
void exec(Callable&& task, Args&&... args);
然后 Callable
将接受几种类型的仿函数,包括 lambda。
请注意,与模板解决方案相比,使用 std::function
对象的性能损失很小。
您还可以添加一个 static_assert 以确保您的 Callable
可以使用给定的参数调用,否则显示有意义的消息,而不是让编译器在实际调用时生成一个.
您可以尝试将 exec 的签名更改为
template<typename Fn, typename... Args>
void exec(Fn&& task, Args&&... args)
然后在函数
中构造你的std::function
lambada 不是 std::function
,因此无法从中推导出类型。您需要将 std::function
部分设为不可推导的上下文:
template<class T>struct identity_t{using type=T;};
template<class T>using identity=typename identity_t<T>::type;
template <typename ResultType>
class Foo
{
public:
template <typename... Args>
void exec(const std::function<ResultType(identity<Args>...)>& task,
Args&&... args)
{}
};
您还可以使用模板约束使函数完全通用;
#include <type_traits>
template<class...>struct voider{using type=void;};
template<class... Ts>using void_t=typename voider<Ts...>::type;
template<class T,class=void>
struct callable:std::false_type{};
template<class F,class... Ts>
using invoker=decltype(std::declval<F>()(std::declval<Ts>()...));
template<class F,class... Ts>
struct callable<F(Ts...),void_t<invoker<F,Ts...>>>:std::true_type{};
template<class R,class S>
using result_eq=std::is_same<std::result_of_t<S>,R>;
#define REQUIRE(cond) std::enable_if_t<(cond)>* = nullptr
template<class R>
class Foo
{
public:
template<class F, class... Args, REQUIRE(callable<F(Args...)>{}), REQUIRE((result_eq<R, F(Args...)>{}))>
void exec(F&& f, Args&&...);
};
int main()
{
Foo<void>().exec([] (int) {}, 4);
}
我确定错误非常简单和愚蠢,但我看不到。这是代码:
#include <future>
template <typename ResultType>
class Foo
{
public:
template <typename ...Args>
void exec(const std::function<ResultType(Args...)>& task, Args&&... args) {}
};
int main()
{
Foo<void>().exec([](){});
return 0;
}
这是错误:
'void CAsyncTask::exec(const std::function &,Args &&...)' : could not deduce template argument for 'const std::function &' with [ ResultType=void ]
Foo<void>().exec<void>([](){})
也不起作用(我更希望不必手动指定 Args
类型)。
关于建议答案的更新:以下代码确实有效。
CAsyncTask<void>().exec(std::function<void ()>([](){}));
但是这个问题真的没有解决方法吗?我能否以某种方式扩展我的模板以推断出 lambda 参数?
正如 Pradhan 提到的,std::function
的确切类型无法从 lambda 中推断出来。您可以显式转换它来解决问题:
Foo<void>().exec(std::function<void()>([](){}));
或者,您可以简单地为函数使用另一个类型名称,而无需创建 std::function
:
template <typename Callable, typename ...Args>
void exec(Callable&& task, Args&&... args);
然后 Callable
将接受几种类型的仿函数,包括 lambda。
请注意,与模板解决方案相比,使用 std::function
对象的性能损失很小。
您还可以添加一个 static_assert 以确保您的 Callable
可以使用给定的参数调用,否则显示有意义的消息,而不是让编译器在实际调用时生成一个.
您可以尝试将 exec 的签名更改为
template<typename Fn, typename... Args>
void exec(Fn&& task, Args&&... args)
然后在函数
中构造你的std::functionlambada 不是 std::function
,因此无法从中推导出类型。您需要将 std::function
部分设为不可推导的上下文:
template<class T>struct identity_t{using type=T;};
template<class T>using identity=typename identity_t<T>::type;
template <typename ResultType>
class Foo
{
public:
template <typename... Args>
void exec(const std::function<ResultType(identity<Args>...)>& task,
Args&&... args)
{}
};
您还可以使用模板约束使函数完全通用;
#include <type_traits>
template<class...>struct voider{using type=void;};
template<class... Ts>using void_t=typename voider<Ts...>::type;
template<class T,class=void>
struct callable:std::false_type{};
template<class F,class... Ts>
using invoker=decltype(std::declval<F>()(std::declval<Ts>()...));
template<class F,class... Ts>
struct callable<F(Ts...),void_t<invoker<F,Ts...>>>:std::true_type{};
template<class R,class S>
using result_eq=std::is_same<std::result_of_t<S>,R>;
#define REQUIRE(cond) std::enable_if_t<(cond)>* = nullptr
template<class R>
class Foo
{
public:
template<class F, class... Args, REQUIRE(callable<F(Args...)>{}), REQUIRE((result_eq<R, F(Args...)>{}))>
void exec(F&& f, Args&&...);
};
int main()
{
Foo<void>().exec([] (int) {}, 4);
}