c++ - 将(部分)模板化模板函数作为 std::function(或函数指针)传递

标签 c++ c++11 templates c++14 function-templates

#include <vector>
#include <functional>

template<class F>
class Foo
{
public:
    template <class T>
    void std_function(std::function<F(std::vector<T>)> functor)
    {
        /* something */
    }

    template <class T>
    void func_ptr(F (*funtor)(std::vector<T>))
    {
        /* something else */
    }
};

template<class T, class F>
F bar(std::vector<T>)
{
    return F();
}

int main()
{
    Foo<double> test;
    std::function<double(std::vector<int>)> barz = bar<int, double>;

    test.std_function(bar<int, double>); //error 1
    test.std_function(barz); //OK 1
    test.func_ptr(bar<int, double>); //OK 2

    test.std_function(bar<int>); //error 2::1
    test.func_ptr(bar<int>); //error 2::2

    return 0;
}

问题 1.

error 1 :我正在尝试将显式实例化的模板函数( bar<int, double> )作为 std::function 传递, 但这是不合法的。

OK 1 :如果我换行 bar<int, double>进入std::function<double(std::vector<int>)>并传递包装仿函数,现在是合法的。

OK 2 行:如果我通过 bar<int, double>通过Foo::func_ptr , 它获取函数指针作为参数而不是 std::function ,也是合法的。

我想让行error 1合法。与 OK 2 行一样,可以传递 bar<int, double>没有任何包装(不像 Line OK 1)并保持相同的形式。但是,参数类型不同。我想传递为 std::function ,不是函数指针。

问题 2.

error 2::1 and 2::2 :我想在这里实现的是,我想要类 Foo推断 bar 的返回类型作为其类模板类型 F (对于上面的代码,Fdouble )。所以我可以传递为 bar<int> , 不是 bar<int, double> .

但是似乎推演失败,因为即使我通过了bar<int>通过Foo::func_ptr ,它仍然会产生错误。我怎样才能让这段代码按照我的意图工作?

最佳答案

对于错误 1,发生的事情是编译器试图替换 std::function 中的 T,但它不能,因为最终是一个函数指针和一个 std::function 是不同的类型,并且没有为指向 std::function

的函数指针定义转换

这有效:

std::function<double(std::vector<int>)> barz = bar<int, double>

因为 std::function 巧妙地使用类型删除编写,以拥有一个构造函数,该构造函数可以接受任何可转换为所需类型的可调用对象。请注意,这与上述错误中的类型推导不同,因为我们已经在此处为 std::function 指定了模板参数。

请注意,我们可以做一些工作来让 Foo::std_function 正常工作。首先更改其签名以获取转发引用:

template <class T>
void std_function(T&& functor){/*I'll talk about this in a bit*\}

然后我们可以在内部构建我们的 std::function(你想要传递给它的是什么,我不知道)通过使用一些辅助结构来确定它的类型。对于函数指针,我们可以执行以下操作:

// base
template<class... T>
struct function_type_impl;

// specialization for function ptrs and static class fns
template<class Ret, class... Args>
struct function_type_impl<Ret(*)(Args...)>
{
   using type = std::function<Ret(Args...)>;
};

// type alias so we don't need to keep typing typename ... ::type
template<class... T>
using function_type = typename function_type_impl<std::decay_t<T>...>::type;

然后我们可以修改我们的std_function 签名:

template <class T>
void std_function(T&& functor)
{
    function_type<T> myFunction = std::forward<T>(functor);
    // do something with our std::function
}

然后你可以称它为

test.std_function(&::bar<int, double>);

但是,如果我们想要更完整,并接受仿函数、lambda 甚至其他 std::functions,我们可以添加更多特化:

namespace detail
{
template<class... T>
struct function_type_impl;

template<class Callable>
struct function_type_impl<Callable>
{
    using type = typename function_type_impl<decltype(&Callable::operator())>::type;
};

template<class C, class Ret, class... Args>
struct function_type_impl<Ret(C::*)(Args...) const>
{
    using type = std::function<Ret(Args...)>;
};

template<class Ret, class... Args>
struct function_type_impl<Ret(*)(Args...)>
{
    using type = std::function<Ret(Args...)>;
};

template<class... T>
using function_type = typename function_type_impl<std::decay_t<T>...>::type;
}// detail namespace

现在下面的也可以工作了:

struct MyFunctor
{
    double operator()(std::vector<int>) const
    {
        return 42;
    }
};

struct MyFunctor2
{
    static double foo(std::vector<int>)
    {
        return 42;
    }
};

int main()
{
    Foo<double> test;
    std::function<double(std::vector<int>)> barz = bar<int, double>;
    test.std_function(&::bar<int, double>);
    test.std_function(barz);
    test.std_function([](std::vector<int>)->double{return 42;});
    test.std_function(MyFunctor{});
    test.std_function(MyFunctor2::foo);
}

Live Demo

对于错误 2::1 和 2::2,问题更简单;函数在完全实例化之前根本不存在。也就是说,您不能创建指向部分模板化函数的函数指针。尝试获取函数指针时,您必须指定所有模板参数。由于您已经指定了返回类型,如果您明确告诉 func_ptr 要为 T 推断什么,则可以允许编译器为您实例化指针的其余部分:

test.func_ptr<int>(bar); 

关于c++ - 将(部分)模板化模板函数作为 std::function(或函数指针)传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41837439/

相关文章:

c++ - 无效使用不完整类型 'PGconn {aka struct pg_conn}'

c++ - 右值引用和构造函数参数

c++ - 在模板模板参数中抛出多模板类 - 模板绑定(bind)?

c++ - 将 float 宏与 int* 一起切换 - 它有什么作用?

java - 为什么要用栈而不是堆?

c++ - 使用 regex_replace() 时,当格式字符串中紧跟另一个数字时,反向引用子匹配的正确方法是什么?

c++ - 多个 emplace_back 额外调用复制构造函数

c++ - 无法创建模板持有对象

c++ - 当我在某些情况下使用模板参数时,编译器如何生成函数实例?

c++ - 避免使用运算符 >> 在空格处中断输入