Heim  >  Fragen und Antworten  >  Hauptteil

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

迷茫迷茫2765 Tage vor589

Antworte allen(2)Ich werde antworten

  • 黄舟

    黄舟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)

    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)

    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;
    }

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

    Antwort
    0
  • StornierenAntwort