传递函数的可变参数函数模板

问题描述 投票:4回答:2

考虑下面的函数模板:

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

编译错误是(GCC-8与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);

使用虚拟模板

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

使得foo0<void, int>(bar<int>);在GCC-8编译罚款,但给出了使用铛与苹果LLVM版本10.0.0(铛-1000.11.45.5)的错误。

铛误差

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) {}
c++ templates c++17 variadic-templates
2个回答
1
投票

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

在计取,当你打电话

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

foo0()foo1()期待一个std::functionbar是一个指针,它指向可以被转换为一个std::function但不是std::function的功能。

foo1()情况下,你都明确和RetType模板ArgType参数,所以编译器可以bar转换为std::function<void(int)>和一切顺利。

foo0()的情况是不同的,因为模板参数ArgTypes...是一个可变参数之一,并呼吁foo0<void, int>(bar)你没有明确完整ArgTypes...可变参数列表,但只有第一种类型。

如果我没有错,问题是,编译器尝试从ArgTypes...参数推断bar的其余部分,但bar不是std::function所以编译器无法推断ArgTypes...的休息,这样的错误。

我想

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

或者干脆

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

或(C ++仅17)还

foo0(std::function{bar});

应该编译,因为,调用foo0()这样一来,该函数接收std::function所以编译器可以完全推导出模板参数。

我不明白如何与bar()版本与虚拟模板参数

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

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


1
投票
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>,你是不是说“RetTypevoidArgTypes...int。你是说ArgTypes...始于int

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::function,并且只要签名完全匹配它的工作原理。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.