简体   繁体   English

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

[英]Passing a function to a variadic function template

Consider the following function templates: 考虑以下功能模板:

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

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

And the following function: 并具有以下功能:

void bar(int n) {}

Why does the following occur: 为什么会发生以下情况:

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

The compilation error is (gcc-8 with C++17): 编译错误是(gcc-8 with C ++ 17):

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

Using a dummy template 使用虚拟模板

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

makes foo0<void, int>(bar<int>); 使foo0<void, int>(bar<int>); compile fine in gcc-8 but gives an error using clang with Apple LLVM version 10.0.0 (clang-1000.11.45.5). 在gcc-8中编译正常,但在使用Apple LLVM 10.0.0版(clang-1000.11.45.5)的clang时出错。

The clang error is 铿锵的错误是

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) {}

Why does the following occur [?] 为什么会出现以下情况[?]

Take in count that when you call 当你打电话时,请记住

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

foo0() and foo1() are expecting a std::function and bar is a pointer to a function that can be converted to a std::function but isn't a std::function . foo0()foo1()期望一个std::functionbar是一个指向函数的指针,该函数可以转换为std::function但不是std::function

In the foo1() case, you explicit both RetType and ArgType template parameters, so the compiler can convert bar to a std::function<void(int)> and all goes well. foo1()情况下,您显式显示RetTypeArgType模板参数,因此编译器可以将bar转换为std::function<void(int)> ,一切顺利。

But the foo0() case is different because the template parameter ArgTypes... is a variadic one and calling foo0<void, int>(bar) you don't explicit the full ArgTypes... variadic list but only the first type. 但是foo0()情况是不同的,因为模板参数ArgTypes...是一个可变参数并调用foo0<void, int>(bar)你没有显式完整的ArgTypes... variadic列表但只有第一种类型。

If I'm not wrong, the problem is that the compiler try to deduce the rest of ArgTypes... from the bar argument but bar isn't a std::function so the compiler is unable to deduce the rest of ArgTypes... , so the error. 如果我没错,问题是编译器试图从bar参数中推导出其余的ArgTypes...但是bar不是std::function因此编译器无法推断出其余的ArgTypes... ,这样的错误。

I suppose that 我想

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

or simply 或者干脆

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

or (C++17 only) also 或者(仅限C ++ 17)

foo0(std::function{bar});

should compile because, calling foo0() this way, the function receive a std::function so the compiler can completely deduce the template parameters. 应该编译因为,这样调用foo0() ,函数接收一个std::function这样编译器就可以完全推导出模板参数。

I don't understand how the version with bar() with a dummy template parameter 我不明白带有虚拟模板参数的bar()的版本如何

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

can compile with g++-8 and I suppose it's a g++ bug. 可以用g ++ - 8编译,我想这是一个g ++ bug。

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

the fix is: 修复是:

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) {}

and now your foo0<void, int>(bar) compiles. 现在你的foo0<void, int>(bar)编译。

The general problem is that saying foo0<void, int> , you aren't saying " RetType is void and ArgTypes... is int . You are saying ArgTypes... starts with int . 一般的问题是说foo0<void, int> ,你不是说“ RetTypevoidArgTypes...int 。你说的是ArgTypes... int 开头

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

the above compiles fine. 以上编译好。

... ...

Another approach in is to add another overload. 另一种方法是添加另一个重载。

Leave this one: 留下这个:

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

but add: 但添加:

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

here we wrap F into a construction-guided std::function . 这里我们将F包装成一个构造引导的std::function

Your call to foo2<int, void>( bar ) now calls foo2<void, int, decltype(bar)&> , the 2nd overload. 你对foo2<int, void>( bar )调用现在调用foo2<void, int, decltype(bar)&> ,第二个重载。 Then it proceeds to construct a std::function from it, and so long as the signatures match exactly it works. 然后它继续从它构造一个std::function ,只要签名完全匹配就可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM