当前位置: 首页 > 知识库问答 >
问题:

无法使用std::function作为参数类型(需要函数指针版本)宁愿像STL一样模板,但这样它就无法推断参数

许远航
2023-03-14

以下代码在Coliru上使用其默认编译参数进行了所有尝试

g-std=c 17-O2-Wall-docantic-pthread-main.cpp

假设我们想制作一个保存数据“容器”的包装器(例如,向量、列表、任何保存一个可以模板化的值的数据结构)。然后,稍后我们要使用助手函数将其转换为另一种类型。这个函数可以被多次调用,只需编写<代码>转换(…) ,而不是像转换

还假设我们有这个(请注意,类似于std::move、explicit、通过std::copy()在convert函数中复制等等)。

尝试1:使用下面的std::function失败:

#include <functional>
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>

using std::string;
using std::vector;

template <template<typename...> class DataContainer, typename T, typename... TArgs>
struct Wrapper {
    DataContainer<T, TArgs...> dataContainer;

    Wrapper(DataContainer<T, TArgs...> container) : dataContainer(container) { }

    // Using parameter type std::function<V(T)> fails here
    template <typename V, typename... VArgs>
    DataContainer<V, VArgs...> convert(std::function<V(T)> f) {
        DataContainer<V, VArgs...> output;
        for (T t : dataContainer)
            output.emplace_back(f(t));
        return output;
    }
};

int main() {
    vector<int> nums = {123};
    Wrapper w{nums};

    vector<string> intVec = w.convert(std::to_string);

    std::cout << intVec.front() << std::endl;
}

这不会编译,并给出错误

主要的cpp:在函数“int main()”中:

main.cpp:37: 57:错误:没有匹配函数调用'Wrapper

     vector<string> intVec = w.convert(std::to_string);

                                                     ^

main.cpp:17: 36:注:候选:'模板DataContainer Wrapper::conversion(std::function)[with V=V; VArgs={VArgs...}; DataContainer=std::向量;T=int; TArgs={std::allocator}]'

     DataContainer<V, VArgs...> convert(std::function<V(T)> f) {

                                ^~~~~~~

main.cpp:17: 36:注意:模板参数推导/替换失败:

main.cpp:37: 57:注意:不匹配的类型'std::function'和'std::__cxx11::string()(长双倍)'{aka'std::__cxx11::basic_string()(长双倍)'}

     vector<string> intVec = w.convert(std::to_string);

                                                     ^

main.cpp:37: 57:注意:不匹配的类型'std::function'和'std::__cxx11::string()(int)'{aka'std::__cxx11::basic_string()(int)'}

主要的cpp:37:57:注意:无法推断模板参数“V”

我尝试创建自己的独立静态函数,以防重载弄乱它,但我得到了同样的错误,即:

不匹配的类型“std::function”和“int()(std::\ucx11::string){aka“int()(std:\ucx11::basic\u string)}

但是,当我将std::函数更改为函数指针时:

// Now using V(*f)(T) instead
template <typename V, typename... VArgs>
DataContainer<V, VArgs...> convert(V(*f)(T)) {
    DataContainer<V, VArgs...> output;
    for (T t : dataContainer)
        output.emplace_back(f(t));
    return output;
}

现在可以打印出来了

123个

这给了我想要的,但现在我必须在convert上指定类型,如。转换

尝试2:

我想我可以做STL库所做的事情,它像这样模板函数:

template <typename Func, typename V, typename... VArgs>
DataContainer<V, VArgs...> convert(Func f) {
    DataContainer<V, VArgs...> output;
    for (T t : dataContainer)
        output.emplace_back(f(t));
    return output;
}

但编译失败:

主要的cpp:在函数“int main()”中:

主要的cpp:45:57:错误:调用“包装器”时没有匹配的函数

     vector<string> intVec = w.convert(std::to_string);

                                                     ^

主要的cpp:33:36:注意:候选者:“模板数据容器包装器::convert(Func)[其中Func=Func;V=V;VArgs={VArgs…};数据容器=标准::向量;T=int;TArgs={std::分配器}]'

     DataContainer<V, VArgs...> convert(Func f) {

                                ^~~~~~~

main.cpp:33: 36:注意:模板参数推导/替换失败:

main.cpp:45:57:注意:无法推导模板参数“函数”

     vector<string> intVec = w.convert(std::to_string);

                                                     ^

我想要的就是排队

vector<string> intVec = w.convert(std::to_string);

无需在convert右侧编写任何模板即可工作。

我做错了什么?我可以用函数指针得到我想要的东西,但似乎我必须编写一系列重载,如V(*f)(T)V(*f)(const

尝试3:

除非我指定类型,否则lambda不起作用,这意味着我必须这样做:

vector<string> intVec = w.convert<string>([](int i) { return std::to_string(i); });

我想:

vector<string> intVec = w.convert([](int i) { return std::to_string(i); });

当我没有指定类型时会发生这种情况(为什么它不能推断出来?)

主要的cpp:在函数“int main()”中:

主要的cpp:45:82:错误:调用“包装器”时没有匹配的函数

     vector<string> intVec = w.convert([](int i) { return std::to_string(i); });

                                                                              ^

main.cpp:17: 36:注:候选:'模板DataContainer Wrapper::conversion(std::function)[with V=V; VArgs={VArgs...}; DataContainer=std::向量;T=int; TArgs={std::allocator}]'

     DataContainer<V, VArgs...> convert(std::function<V(T)> f) {

                                ^~~~~~~

main.cpp:17: 36:注意:模板参数推导/替换失败:

主要的cpp:45:82:注意:“main()::”不是从“std::function”派生的

     vector<string> intVec = w.convert([](int i) { return std::to_string(i); });

                                                                              ^

简而言之,所有这些都是因为我必须不断复制粘贴每个使用函数指针和std::f块重载执行此类操作的函数,然后如果我想支持右值、左值、const左值的函数参数...我得到了函数的组合爆炸,这使得工作解决方案一团糟。我不知道这是不可避免的,还是我错过了什么让它推断类型,同时允许我传入函数名称或lambda


共有1个答案

江永安
2023-03-14

这是每个人都会犯的错误:

template <typename V>
DataContainer<V> convert(std::function<V(T)> f);

我理解人们为什么这么做,这显然是正确的做法,对吗?甚至在这里写了很多。关键是:推导std::函数总是错误的。您无法推断这一点,因为它不允许转换,即使您可以推断,它也会增加您不想要的开销。

不幸的是,没有等效的干净语言解决方案。我所说的清洁是指,在某种程度上,T和V是如此清晰地联系在一起,就像上面的非溶液一样。

你真正想做的是:

template <typename F, typename V = std::invoke_result_t<F, T>>
DataContainer<V> convert(F f);

也就是说,我们只是推断类型——然后使用类型特征来找出答案。

std::to\u string的问题是完全独立的-您不能将重载的函数或函数模板传递到函数模板中。你总是要把它包在一个小羊肚里。

 类似资料:
  • 我的分类测试应用程序有一个问题,我使用了一个比较器。我收到一条信息: 线程“主”java.lang 中的异常:未解决的编译问题:无法推断排序器的类型参数 对于该代码: 分拣机类: 可比接口: id比较器类: 比较器接口: 这样用有什么错?我怎样才能做得更好?

  • 我仍然在玩我的日历,我已经设法将 https://github.com/SundeepK/CompactCalendarView 整合到我的一个片段中。只剩下一个错误,我做了一些研究,其他人也遇到了问题,例如使用ArrayList 示例代码: IDE说: 注:C:...\Uebersicht.java使用或覆盖不推荐使用的API。注意:用-Xlint:deprecation重新编译以获得详细信息。

  • 我目前有一个,但是为了灵活性,我希望能够分配一个lambda表达式,将作为映射中的值返回。 所以我创建了这个模板类: 并像这样使用它: IntelliSense提供了更多信息: 多个操作符“=”匹配这些操作数:function“valueorfunction::operator=(const std::function&other)[with T=std::wstring]”function“va

  • 我需要 Kotlin 中的一个集合来仅包含实现给定接口的元素。 例如:包含动物集合的地图: 通过阅读文档、博客和SO问题,我编写了使用Generics in关键字的代码: 现在我想在Test类中添加一个读取“data”内容的方法,例如将其打印到控制台: 如果我这样做,我会遇到编译错误: 我的解决方案是强制我的动物进入一个ArrayList 但是我不确定这是编写这种代码的最好方式。有没有更好的方式告

  • 我最近使用Kotlin开发了Android应用程序,但我收到以下错误 无法推断此参数的类型。请明确指定 在我的课堂下面,我收到了错误。

  • std=C14的g在函子类的模板方法(本身不是模板)上给我一个“无法推断模板参数'Key'”错误。我不知道为什么。代码看起来应该可以工作。 我正在实现一个2-3树,它有一个采用函子的层次顺序遍历方法。操作人员tree23代码基本上是这样的: 级别顺序遍历调用仿函数的函数调用操作符,向其传递两个参数。 函子非常简单: