正在学习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
一起用,會有一種動態語言的錯覺。