我仍在努力学习一些C ++语法 这次我想用lambda添加额外的参数。但是为了使代码通用,我可以接受任何函数及其参数:
#include <functional>
#include <exception>
template<typename R>
class Nisse
{
private:
Nisse(Nisse const&) = delete;
Nisse(Nisse&&) = delete;
Nisse& operator=(Nisse const&) = delete;
Nisse& operator=(Nisse&&) = delete;
public:
//Nisse(std::function<R()> const& func) {} // disable for testing
template<typename... Args>
Nisse(std::function<R(Args...)> const& func, Args... a) {}
};
int main()
{
// I was hoping this would deduce the template arguments.
Nisse<int> nisse([](int a,double d){return 5;},12,12.0);
}
这会产生:
> g++ -std=c++0x Test.cpp
Test.cpp:21:61: error: no matching function for call to ‘Nisse<int>::Nisse(main()::<lambda(int, double)>, int, double)’
Test.cpp:21:61: note: candidate is:
Test.cpp:16:9: note: template<class ... Args> Nisse::Nisse(const std::function<R(Args ...)>&, Args ...)
我尝试明确指定模板类型:
Nisse<int> nisse<int,double>([](int a,double d){return 5;},12,12.0);
但是这(对我来说很惊讶)是语法错误:
> g++ -std=c++0x Test.cpp
Test.cpp: In function ‘int main()’:
Test.cpp:21:23: error: expected initializer before ‘<’ token
Test.cpp:21:65: error: expected primary-expression before ‘,’ token
Test.cpp:21:73: error: expected ‘;’ before ‘)’ token
答案 0 :(得分:4)
您无法从lambda推断std::function
模板参数。接受任意可调用的通常方法是通用参考:
template<typename F, typename... Args,
typename = typename std::enable_if<std::is_convertible<
decltype(std::declval<F>()(std::declval<Args>()...)), R>::value>::type>
Nisse(F &&f, Args... a): Nisse(std::function<R()>(std::bind(f, a...))) {}
此处使用最终(匿名,默认)模板参数来验证第一个模板参数是否类似函数并返回类型为R的结果。
std::enable_if<std::is_convertible<
decltype(std::declval<F>()(std::declval<Args>()...)), R>::value>::type
如以下评论中所描述的@Yakk上面的表达式检查F是一个函数类型,其结果是R.如果它工作,那么一切都很好。如果失败则会生成编译时错误(注意:这使用SFINAE)。
对于方法,SFINAE被插入到返回类型中,但对于构造函数,这不是一个选项;历史上添加了一个额外的默认构造函数参数,但添加一个默认模板参数更加优雅,因为它根本不会更改构造函数签名。 SFINAE的匿名模板参数在可变参数模板参数后特别有吸引力,因为用户无法(意外地?)覆盖默认值。