C++11 以std::function<void()> 做非类型模板的参数类型为什么会报错呢?
正在学习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除了能包装匿名函数外,还有什么情况下与函数对象或者普通函数指针表现不同呢?谢谢。
Answers
你的写法,
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
一起用,会有一种动态语言的错觉。
因为非类型模板实参必须是常量表达式。因为模板中的东西都必须是可在编译期确定的。
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)
std::function
不能作为非类型模板参数的类型。
wrapper<f>()
中 f是一个变量,它的值是运行期才能确定的。所以模板没办法编译通过,总不能等到运行时再Instantiate出一个
wrapper<f>
函数,让其中的i指向f吧?JIT?木有。所以做不了。
PS:你试试能不能搞出
constexpr std::function<x>
前面说指针类型可以作为非类型模板参数的类型,所以
<void i()>
可以编译通过。函数名可保证是
constexpr
,所以
wrapper<hello>
不会报错。对于Pointer,我想不出其它有意义的示例,下面换成全局静态变量得到
constexpr pointer
就能编译成功了:
template <typename std::function<void()> *i>
void wrapper() {
(*i)();
}
static std::function<void()> f=hello;
int main() {
wrapper<&f>();
//下面是合法的,但在GCC下却编译不成功,说是因为GCC的Bug
//要不你用VC++试试?
constexpr std::function<void()> *f3=&f;
wrapper<f3>();
//顺便说,下面也能编译成功,只是运行当然是segmentfault啦
constexpr std::function<void()> *f2=0;
wrapper<f2>();
return 0;
}