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除了能包装匿名函数外,还有什么情况下与函数对象或者普通函数指针表现不同呢?谢谢。

c++11 模板 C++

tkfkid 11 years, 8 months ago

你的写法, 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 一起用,会有一种动态语言的错觉。

samchen answered 11 years, 8 months ago

因为非类型模板实参必须是常量表达式。因为模板中的东西都必须是可在编译期确定的。

Non-type template parameter type is one of the following types (optionally cv-qualified, the qualifiers are ignored)

  1. integral type
  2. enumeration
  3. pointer to object or to function
  4. lvalue reference to object or to function
  5. pointer to member object or to member function
  6. 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;
}

XOX.... answered 11 years, 8 months ago

Your Answer