我需要模板函数,它将使用从 std::tuple
获取的参数调用其他函数。
我写了一些代码,可以正确编译:
#include <tuple>
template<typename ...ARGS1, typename ...ARGS2>
void foo(const std::tuple<ARGS1...> &, const std::tuple<ARGS2...> &)
{
//call function
}
int main()
{
std::tuple<int, bool> tuple1(0, false);
std::tuple<double, void*, float> tuple2(0.0, nullptr, 0.0f);
foo(tuple1, tuple2);
return 0;
}
现在我需要再添加一个指向函数的参数:
#include <tuple>
template<typename ...ARGS1, typename ...ARGS2>
void foo(const std::tuple<ARGS1...> &, const std::tuple<ARGS2...> &, void (*)(ARGS1..., ARGS2...))
{
//call function
}
void bar(int, bool, double, void*, float)
{
}
int main()
{
std::tuple<int, bool> tuple1(0, false);
std::tuple<double, void*, float> tuple2(0.0, nullptr, 0.0f);
foo(tuple1, tuple2, &bar);
return 0;
}
我尝试以多种不同的方式进行操作,但编译器总是返回模板参数推导/替换失败
和不一致的参数包推导与''和''
。我不明白,我的代码有什么问题。编译器输出中没有任何有用的信息。
有人可以帮我正确地写这个吗?
最佳答案
由于 clang++ 在这种情况下表现出不同(且奇怪)的行为,所以我不完全确定这个答案。不过,我想我可以解释一下使用 g++ 时发生了什么。
让我们看下面的例子:
#include <iostream>
template<typename ...ARGS1, typename ...ARGS2>
void foo(void (*)(ARGS1..., ARGS2...))
{
std::cout << __PRETTY_FUNCTION__ << "\n";
}
void bar(int, bool, double, void*, float) {}
int main() {
foo(&bar);
}
__PRETTY_FUNCTION__
是一个非标准的宏,我经常将其用作 hack 以允许打印编译器推断出的类型。在这种情况下,when compiled with g++ ,程序打印:
void foo(void (*)(ARGS1 ..., ARGS2 ...))
[with ARGS1 = {}; ARGS2 = {int, bool, double, void*, float}]
正如我们所见,g++ 没有为 ARGS1
推导出任何东西。 , 但推导出bar
的函数参数的所有类型对于 ARGS2
.
模板参数推导分别适用于每个函数参数。如果模板参数 T
从两个函数参数推导出来,推导的类型必须相同(除了少数异常(exception))。同样,对于参数包。考虑:
template<typename T>
void foo(T, T) {}
foo(1, 2.3)
这不会编译,因为对于第一个函数参数,T
推导为 int
,但对于第二个函数参数,T
推导为 double
.
同样,
template<typename... T>
void foo(tuple<T...>, tuple<T...>) {}
foo(tuple<int, double>{}, tuple<bool, char>{})
扣除T...
结果不一致对于第一个和第二个函数参数。这种不一致会导致编译错误。
正如我们在此答案的第一部分中所见,对于如下参数:
template<typename ...ARGS1, typename ...ARGS2>
void foo(void (*)(ARGS1..., ARGS2...))
ARGS1
根本不会推导,ARGS2
将“吃掉”您传入的参数的所有函数参数类型。如果我们现在推断ARGS2
同样来自另一个函数参数:
template<typename ...ARGS1, typename ...ARGS2>
void foo(void (*)(ARGS1..., ARGS2...), tuple<ARGS2...>)
然后,推导结果为ARGS2
必须保持一致:
void bar(int, double);
tuple<int, double> t;
foo(&bar, t); // works
tuple<double> u;
foo(&bar, u) // fails: inconsistent deduction results.
使 OP 中的代码编译的一种非常粗略的方法是告诉编译器不要推导 ARGS2
来自第三个参数。这可以通过将第三个参数的类型放在 non-deduced context(不进行推导的上下文)中来实现:
template<typename T>
struct identity_t
{ using type = T; };
template<typename T>
using non_deduced_context = typename identity_t<T>::type;
template<typename ...ARGS1, typename ...ARGS2>
void foo(const std::tuple<ARGS1...> &, const std::tuple<ARGS2...> &,
non_deduced_context<void (*)(ARGS1..., ARGS2...)>)
{
//call function
}
对于类似 some_class_template<T>::nested_type
的参数, T
处于非推导上下文中。上面的示例中使用了它,但使用了一些语法糖。
不过,在 C++ 中将一个函数传递给另一个函数的常用方法是不同的:
template<typename F>
void foo(F f)
{
//call function, e.g.
f(42);
}
这允许传递任意函数对象,包括指向函数的指针。
关于c++ - 添加参数后“模板参数推导/替换失败”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27712186/