代码之家  ›  专栏  ›  技术社区  ›  dtell

将函数传递给可变函数模板

  •  4
  • dtell  · 技术社区  · 7 年前

    考虑以下函数模板:

    template<typename RetType, typename... ArgTypes>
    void foo0(std::function<RetType(ArgTypes...)> f) {}
    
    template<typename RetType, typename ArgType>
    void foo1(std::function<RetType(ArgType)> f) {}
    

    以及以下功能:

    void bar(int n) {}
    

    为什么会出现以下情况:

     foo0<void, int>(bar);  // does not compile
     foo1<void, int>(bar);  // compiles fine
    

    编译错误是(带有C++17的gcc-8):

    error: no matching function for call to 'foo0<void, int>(void (&)(int))'
       foo0<void, int>(bar);
                          ^
    note: candidate: 'template<class RetType, class ... ArgTypes> void foo0(std::function<_Res(_ArgTypes ...)>)'
     void foo0(std::function<RetType(ArgTypes...)> f) {}
          ^~~~
    note:   template argument deduction/substitution failed:
    note:   mismatched types 'std::function<void(_ArgTypes ...)>' and 'void (*)(int)'
       foo0<void, int>(bar);
    

    使用虚拟模板

    template<typename T>
    void bar(int n) {}
    

    使 foo0<void, int>(bar<int>); 在gcc-8中编译良好,但在Apple LLVM版本10.0.0(clang-1000.11.45.5)中使用clang时出现错误。

    叮当作响的错误是

    error: no matching function for call to 'foo0'
      foo0<void, int>(bar<int>);
      ^~~~~~~~~~~~~~~
    note: candidate template ignored: could not match 'function<void (int, type-parameter-0-1...)>' against 'void (*)(int)'
    void foo0(std::function<RetType(ArgTypes...)> f) {}
    
    2 回复  |  直到 7 年前
        1
  •  2
  •   max66    7 年前

    为什么会出现以下情况[?]

    你打电话的时候要考虑一下

     foo0<void, int>(bar); // compilation error
     foo1<void, int>(bar); // compile
    

    foo0() 和 foo1() 你在等一个 std::function 和 bar 是指向可以转换为 std::函数 但这不是一个 std::函数 .

    在 foo1() 凯斯,你们两个都清楚 RetType 和 ArgType 模板参数,以便编译器可以转换 酒吧 到 std::function<void(int)> 一切顺利。

    但是 foo0() 大小写不同,因为模板参数 ArgTypes... 这是一个变量和调用 foo0<void, int>(bar) 你没有明确说明全部内容 ArgTypes。。。 变量列表,但只有第一种类型。

    如果我没有错的话,问题是编译器试图推断出 ArgTypes。。。 从 酒吧 争论但是 酒吧 这不是一个 std::函数 因此,编译器无法推断出 ArgTypes。。。 ,那么错误就来了。

    我想

    foo0<void, int>(std::function<void(int)>{bar});
    

    或者干脆

    foo0(std::function<void(int)>{bar});
    

    或者(仅限C++17)也可以

    foo0(std::function{bar});
    

    应该编译,因为 foo0() 这样,函数将接收一个 std::函数 因此编译器可以完全推断模板参数。

    我不明白这个版本 bar() 使用伪模板参数

    foo0<void, int>(bar<int>);
    

    可以用g++-8编译,我想这是一个g++错误。

        2
  •  1
  •   Yakk - Adam Nevraumont    7 年前
    template<typename RetType, typename... ArgTypes>
    void foo0(std::function<RetType(ArgTypes...)> f) {}
    

    解决办法是:

    template<class X>struct tag_t{using type=X;};
    template<class X>using block_deduction = typename tag_t<X>::type;
    
    template<typename RetType, typename... ArgTypes>
    void foo0(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}
    

    现在你的 foo0<void, int>(bar) 编译。

    总的问题是 foo0<void, int> “你不是说” RetType 是 void 和 ArgTypes... 是 int .你是说 ArgTypes。。。 从 智力 .

    std::function<void(int, double)> x;
    foo0<void, int>( x )
    

    以上内容很好。

    ...

    另一种方法 就是增加另一个过载。

    留下这个:

    template<typename RetType, typename... ArgTypes>
    void foo2(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}
    

    但补充说:

    template<typename RetType, typename... ArgTypes, class F>
    void foo2(F&& f) {
      return foo2<RetType, ArgTypes...>( std::function{std::forward<F>(f)} );
    }
    template<int unused, class F>
    void foo2(F&& f) {
      return foo2( std::function{std::forward<F>(f)} );
    }
    

    我们包起来了 F 变成了一个建筑指南 std::function .

    你的电话 foo2<int, void>( bar ) 现在通话 foo2<void, int, decltype(bar)&> ,第二次过载。然后它继续构建一个 std::函数 只要签名完全匹配就行。