std::function模板参数解析

8

我目前正在开发一个库,在该库中我正在链接函数对象。

我正在创建一个函数模板,它接受可调用对象 (目前是 std::function),并在函数的输出和输入类型上进行参数化。这里是我所定义的简化版本:

template <typename In, typename Out>
std::vector<Out> process(std::vector<In> vals, std::function< Out(In) > func)
{
    // apply func for each value in vals
    return result;
}

我遇到的问题与用法有关。当我传递一个lambda表达式时,编译器无法正确推断类型,因此会抱怨该函数不存在:

std::vector<string> strings;
// does NOT compile
auto chars = process(strings,
        []( std::string s ) -> char
        {
            return s[0]; // return first char
        }
);

如果我明确地将lambda表达式包装在std::function中,程序就可以编译:
std::vector<string> strings;
// DOES compile
auto chars = process(strings,
        std::function< char(std::string) >(
        []( std::string s ) -> char
        {
            return s[0]; // return first char
        })
);

我还没有测试过传递函数指针或函数对象,但如果我不直接传递显式的std::function对象,似乎编译器很难推断In和Out参数。我的问题是:是否有办法绕过这个问题,以便在调用处不显式地提及输入/返回类型而推断出可调用对象的输入/返回类型?也许可以将模板参数化为函数类型,而不是输入/返回类型?基本上,我需要推断任意可调用对象的In和Out类型。也许有一些auto/decltype技巧可以用于模板函数的返回类型?谢谢。

这只是一个例子,但这正是std::transform所做的。 - Jon
1
是的,我完全意识到这一点。Transform函数适用于迭代器,但我需要创建另一个对象,该对象的参数基于转换的返回类型。据我所知,transform函数不需要这些类型,因为它可以只执行*out = *in++; ++out;而不必知道value_types的值,因为它只处理迭代器。这个例子简化了很多事情-要求了解输入/返回类型。 - Alexander Kondratskiy
2个回答

9
我认为您可以创建一个中间返回类型推导函数,该函数使用 decltype 确定要传递给实际函数对象的参数:
template <typename Out, typename In>
std::vector<Out> process_intern(std::vector<In> vals, std::function< Out(In) > func)
{
    // whatever
}

template <typename In, typename Func>
auto process(std::vector<In> vals, Func func) -> std::vector<decltype(func(vals[0]))>
{
    return process_intern<decltype(func(vals[0]))>(vals, func);
}

当然,除非有理由需要类型擦除函数类型,否则您可能希望考虑直接在process()中实现逻辑。

很棒!你提出了关于类型擦除的好观点,我不认为我需要它。获取返回类型的妙招,谢谢! - Alexander Kondratskiy
编译器通过某种方式找出decltype(func(vals[0]))template<typename Out, typename In> process_intern的参数列表相匹配。我之前并不知道编译器能够以这种方式从非平凡的decltype表达式推断出模板参数。这是一个令人惊讶的技巧。 - damienh
@damienh:decltype(func(vals[0])) 的作用只是找到 func(vals[0]) 的返回类型。然而,在调用 process_intern() 时,所得到的类型是明确指定的。 - Dietmar Kühl

8
有没有办法绕过这个问题,以便我可以在不显式提及它们的情况下推断可调用对象的输入/返回类型?
不行。用户定义的转换不会在模板参数推导中考虑。编译器必须提供InOut,使得参数的类型和参数的类型几乎完全匹配,但在这种情况下永远不会匹配。
或许应该将模板参数化为函数类型而不是输入/返回类型。
是的,这通常是所做的(例如查看标准库算法)。

谢谢,关于用户定义转换的内容很有道理。 - Alexander Kondratskiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接