使用C ++ 11可变参数模板进行奇怪的替换失败

时间:2013-11-24 07:22:00

标签: c++11 variadic-templates

我很难弄清楚这个示例代码中导致替换失败的原因:

bool f(int a, int b, float c)
{
    printf("%d %d %f", a, b, c);
    return true;
}

template <typename ...Params>
void call1(Params... params, std::function<bool(Params...)> func)
{
    func(params...);
}

template <typename ...Params>
void call2(std::function<bool(Params...)> func)
{
}

主要地方:

call1<int, int, float>(3, 4, 5.5, f); // Ok.
call2<int, int, float>(f); // Substitution failure.

编译器说:

template argument deduction/substitution failed: mismatched types 'std::function<bool(Params ...)>' and 'bool (*)(int, int, float)'
call2<int, int, float>(f);
                          ^

令我感到困惑的是call1有效,而call2没有。 有小费吗? =)

3 个答案:

答案 0 :(得分:3)

首先:您可以指定少于您使用的参数,让编译器推断出其余参数:

template <typename ...Params>
void func1(Params... params);

func1<int, int>(1, 2, 3); // calls func1<int, int, int>

这意味着Params在您调用时仍然可以添加额外的类型。但是如果你拿到函数地址,就会定义并关闭它:

auto x = func1<int, int>;
x(1, 2, 3); // not possible

直接拨打call1功能时:

template <typename... Params>
void call1(Params... params, std::function<bool(Params...)> func);

call1<int, int, int>(1, 2, 3, f);
call1<int>(1, 2, 3, f); // same as before
call1(1, 2, 3, f); // same as before

编译器能够推断出你有3个整数,因为你刚给他发了3个整数。这样,最后一个参数必须是std::function<bool(int, int, int)>,因为我们完全推断出Params...的含义,并且没有更多类型的空间。

现在有问题的案例:

template <typename... Params>
void call2(std::function<bool(Params...)> func);

call2<int, int, int>(f);

在这里,您告知编译器Params的前3个元素都是整数。 Params = {int, int, int, ...}。请注意,如果扣除这样说,它仍然可以添加其他内容。替换我们:std::function<bool(int, int, int, ...)> func。除非您明确传递std:function(完全匹配),否则编译器可能无法知道此不完整类型的含义。它还不知道它可以有一个构造函数来获取你提供的函数指针,因此存在不匹配。现在,编译器没有足够的数据来决定是否需要更多类型Params。故障。

但请注意这个有趣的案例:

auto x = call2<int, int, int>;
x(f); // x is exactly void(*)(std::function<bool(int, int, int)>). No doubts.

在这里你强迫Params完成。没有扣除评估。虽然很难看,但这也有效:

(&call2<int, int, int>)(f);

答案 1 :(得分:1)

编译器无法使用当前代码推断出类型(即使函数指针可隐式转换为std::function)。您可以创建一个traits类来帮助推断出正确的类型。

template <typename... Params>
struct func_traits {
    using func_type = std::function<bool(Params...)>;
};

template <typename ...Params>
void call2(typename func_traits<Params...>::func_type func) {}

答案 2 :(得分:0)

表达式f的类型是指向函数的指针,即bool (*)(int, int, float)。然而,call2模板的显式实例化具有类型void foo (std::function<bool (int, int, float)> ),即参数类型不同,因此不匹配。

解决方法:

auto x = call2<int, int, float>;
x(f);

利用从函数指针构造std::function的可能性。