Rumah > Soal Jawab > teks badan
正在学习C++11的新特性,用非类型模板写了一个函数包装器,我是这样写的:
#include <iostream>
#include <cstdlib>
#include <string>
#include <functional>
void hello() {
std::cout << "Hello, world!\n";
return;
}
template< std::function<void()> i>
void wrapper() {
i();
}
int main() {
std::function<void()> f = hello;
wrapper<f>();
return 0;
}
在VS2013上编译错误,提示是
“std::function”: 非类型模板参数“i”的类型非法
但是当我将wrapper的定义改成
template<void i()>
void wrapper() {
i();
}
将调用改成wrapper<hello>();
之后编译运行就一切正常了。请问这是什么原因?
另外请问std::function除了能包装匿名函数外,还有什么情况下与函数对象或者普通函数指针表现不同呢?谢谢。
黄舟2017-04-17 11:40:24
因为非类型模板实参必须是常量表达式。因为模板中的东西都必须是可在编译期确定的。
http://en.cppreference.com/w/...
Non-type template parameter type is one of the following types (optionally cv-qualified, the qualifiers are ignored)
integral type
enumeration
pointer to object or to function
lvalue reference to object or to function
pointer to member object or to member function
std::nullptr_t (since C++11)
wrapper<f>()
中 f是一个变量,它的值是运行期才能确定的。所以模板没办法编译通过,总不能等到运行时再Instantiate出一个wrapper<f>
函数,让其中的i指向f吧?
For pointers to functions, the valid arguments are pointers to functions with linkage (or constant expressions that evaluate to null pointer values).,所以<void i()>
可以编译通过:
template <typename std::function<void()> *i>
void wrapper() {
(*i)();
}
// 将static 换成 extern 才能通过 MS C++
static std::function<void()> f=hello;
int main() {
wrapper<&f>(); // G++ OK
//下面的不行
constexpr std::function<void()> *f3=&f;
wrapper<f3>();
//顺便说,下面也能编译成功,只是运行当然是segmentfault啦
constexpr std::function<void()> *f2=0;
wrapper<f2>();
return 0;
}
伊谢尔伦2017-04-17 11:40:24
你的写法, template < std::function<void()> i >
,这里的 i
明显是一个变量,而不是类型,如果要声明类型应该写成 template <typename Func>
。不过如果声明为类型,wrapper
当然就没法工作了,因为 i()
就相当于实例化一个空的 std::function
对象,并没有做任何事情,最终当然就不能得到你想要的效果。
一般来说,你应该这样实现 wrapper
才正常。
template <typename Func>
void wrapper(Func func) {
func();
}
std::function
最大的功能是表达匿名函数,特别是 []
里面捕捉了当前上下文变量的匿名函数,结合着 std::shared_ptr
一起用,会有一种动态语言的错觉。