无法将std :: function用作参数类型(需要函数指针版本),宁可使用STL这样的模板,但无法推断出参数

时间:2019-02-08 00:47:01

标签: c++ templates c++17

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

  

g ++ -std = c ++ 17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

假设我们要创建一个包装器来保存数据的“容器”(例如vectorlist,任何保存一个可以模板化的值的数据结构)。然后,稍后我们要使用辅助函数convert将其转换为另一种类型。该函数可以被调用很多,只写convert(...)而不是像convert<TypeToConvertTo>(...)那样做模板会很好。在非MCVE中,我能够做到这一点,但是它需要std::function参数和函数指针参数的绝对复制复制...这是不好的(除非它是必要的邪恶?希望不是)

还要假设我们有这个(请注意,诸如std::moveexplicit之类的东西,通过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;
}

这无法编译,并显示错误

  

main.cpp:在函数'int main()'中:

     

main.cpp:37:57:错误:没有匹配的函数来调用“包装器> :: convert()”

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

                                                     ^
     

main.cpp:17:36:注意:候选:'模板DataContainer包装器:: convert(std :: function)[with V = V; VArgs = {VArgs ...}; DataContainer = std :: vector; 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()(long double)'不匹配的类型{aka'std :: __ cxx11 :: basic_string ()(long double)'}

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

                                                     ^
     

main.cpp:37:57:注意:类型'std :: function'和'std :: __ cxx11 :: string()(int)'{aka'std :: __ cxx11 :: basic_string( )(int)'}

     

main.cpp:37:57:注意:无法推断出模板参数“ V”

在过载引起混乱的情况下,我尝试制作自己的独立静态函数,但是我遇到了同样的错误,说:

  

类型'std :: function'和'int()(std :: __ cxx11 :: string)'{aka'int()(std :: __ cxx11 :: basic_string)'}不匹配的类型

但是,当我使用以下命令将std::function更改为函数指针时:

// 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<TypeHere>(...)的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;
}

但是编译失败并显示:

  

main.cpp:在函数'int main()'中:

     

main.cpp:45:57:错误:没有匹配的函数可以调用“包装器> :: convert()”

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

                                                     ^
     

main.cpp:33:36:注意:候选:'template DataContainer Wrapper :: convert(Func)[with Func = Func; V = V; VArgs = {VArgs ...}; DataContainer = std :: vector; T = int; TArgs = {std :: allocator}]'

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

                                ^~~~~~~
     

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

     

main.cpp:45:57:注意:无法推断出模板参数'Func'

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

                                                     ^

我想要的只是该行

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

不需要我在convert右边写任何模板就可以工作。

我在做什么错?我可以通过函数指针得到想要的东西,但是似乎我必须写一堆重载,例如V(*f)(T)V(*f)(const &T)

尝试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); });

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

  

main.cpp:在函数'int main()'中:

     

main.cpp:45:82:错误:没有匹配的函数可以调用“包装器> :: convert(main()::)”

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

                                                                              ^
     

main.cpp:17:36:注意:候选:'模板DataContainer包装器:: convert(std :: function)[with V = V; VArgs = {VArgs ...}; DataContainer = std :: vector; T = int; TArgs = {std :: allocator}]'

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

                                ^~~~~~~
     

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

     

main.cpp:45:82:注意:'main()::'并非源自'std :: function'

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

                                                                              ^

简而言之,所有这些操作都已完成,因为我必须保持复制粘贴每个使用函数指针和std :: function重载进行此操作的函数,然后如果我想支持右值的函数参数,左值,常量左值...我得到了函数的组合爆炸,这使工作解决方案变得一团糟。我不知道这是否不可避免,或者是否缺少某种东西来推断类型,同时允许我传入函数名或lambda。

1 个答案:

答案 0 :(得分:1)

这是每个人一直在犯的错误:

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

我了解人们为什么这么做,这显然是正确正确的做法吗?甚至写过here的内容。关键是:推论std::function总是错误的。您无法推断出这一点,因为它不允许进行转换,即使您可以推断出它,也会增加您不想要的开销。

不幸的是,没有等效的干净语言解决方案。而且,我的意思是说,TV如此紧密地绑在一起,就像上面的非解决方案一样。

您真正想做的是:

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

也就是说,我们只推导类型-然后使用类型特征确定答案是什么。


std::to_string的问题是完全独立的-您无法将重载的函数或函数模板传递到函数模板中。您始终必须将其包装在lambda中。