c++ - 添加参数后“模板参数推导/替换失败”

标签 c++ templates c++11 template-argument-deduction

我需要模板函数,它将使用从 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/

相关文章:

c++ - 头文件中的源代码

c++ - 如何从源 vector <> 构建搜索结果的 vector <>?

c++ - (Winapi C++) 如何在没有全局变量的情况下将数据从一个窗口传递到另一个窗口?

c++ - 2个小错误,我无法用C++理解!

c++ - 如何编写一个错误结构,它可以包含不同的强类型枚举作为错误代码?

c++ - 如何将包含 double 的 std::string 转换为 double vector ?

c++ - 对象指针和超出范围的对象的 vector

c++ - 模板有问题。错误 : Cannot deduce template argument for type 'T'

c++ - 比较模板类中的枚举 - 它安全吗?

c++ - 模板模板时强制特定重载