首頁  >  問答  >  主體

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 天前590

全部回覆(2)我來回復

  • 黄舟

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

    回覆
    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 一起用,會有一種動態語言的錯覺。

    回覆
    0
  • 取消回覆