将一个模板函数作为参数传递给另一个函数。

3

问题

我正在尝试编写一个函数,它可以在参数包中搜索特定类型,然后创建一个lambda表达式,使用该类型作为模板参数调用另一个函数,例如:

auto fn = findType<MyType, SomeType, OtherType>("OtherType");
fn(otherFn) == otherFn<OtherType>();

我想写出像这样的文章:

template<class T, class ...Ts>
auto findType(const std::string& name) {
    if (refl::is_reflectable<T>() && refl::reflect<T>().name == name) {
        return []<class Fn>(Fn fn) {
            fn<T>();
        };
    }
    return findType<Ts...>(name);
}

然而,C++似乎无法识别fn可以带有模板类型的参数。
我使用的是gcc10和C++20,所以如果可能的话,我也可以使用概念。
我相信问题可以总结为:如何将一个模板参数化的函数传递到另一个函数中?
template<class C>
void fn() {}

template<class Fn, class Arg>
void mainFn(Fn fn) {
    fn<Arg>(); // ???
}

尝试的搜索

我曾经查看过模板模板参数,但那似乎只适用于对类型进行模板化,而不是函数调用。

我也看过 C++ 概念,但 std::invokable 不接受模板参数,要求也似乎不允许这样的表达式:

return []<class Fn>(Fn fn) requires requires { fn<T>(); } {

你必须把模板函数封装成一个lambda(每当传递给另一个函数时)。或者一开始就将其设置为全局lambda。 - HolyBlackCat
从我的测试来看,全局lambda确实可以工作,但模板参数必须被推导出来,例如 auto fn = []<class C>(C c) {};。类似 auto fn = []<class C>() {}; 的写法似乎不起作用,这也是我在这个问题中遇到的情况。 - Moon Cheesez
1
我认为在findType中返回lambda表达式不是一个好选择。函数模板的不同实例中的lambda表达式类型不同,这使得findType的返回类型不匹配。 - RedFog
4个回答

6

函数参数是变量。不是变量模板;只是普通的变量。非模板变量不能获得模板参数。

你无法传递函数模板。你只能传递一个函数模板的特定实例化。你能够接近你想要的是传递一个具有模板化operator()重载的类型,但除非你可以通过推断提供模板参数,否则唯一调用它的方法是通过fn.operator()<TemplateArguments>(params)。所以你最好给它一个有意义的名字。


你如何通过推导提供模板参数?当你说“最好给它一个有意义的名称”时,你指的是什么? - Moon Cheesez
1
@MoonCheesez 让你的 fn 成为一个类的实例,其中包含一个函数模板,最好有一个有意义的名称(例如 call),而不是 operator()。然后使用 fn.call<T>() - RedFog
@MoonCheesez: "如何通过推导提供模板参数?" 当您调用模板函数而没有直接指定所有模板参数时,将发生模板参数推导。其中一些参数是从您提供的函数参数的类型中推导出来的。这是调用模板函数最常见的方式。 - Nicol Bolas

0

对于C++,将一个函数传递给另一个函数的实用方法如下:

#include <iostream>
#include <functional>

// To build and run the code
// g++ -std=c++20 -O3 -Wall -Werror -Wshadow -pedantic file.cpp -o file && ./file

double add5(double input_value) {
    return input_value + 5;
}

template<typename T>
T add2(T input_value) {
    return input_value + (T)2;
}

// std::function<double(double)> func
// return type ----^      ^---------------\
// list of `func` input argument types ---/
double func(double input_value, std::function<double(double)> func) {
    return func(input_value);
}

int main() {
    double value1 = 10.75;
    double value2 = add2(value1);

    std::cout << "Value1: " << value1 << std::endl;
    std::cout << "Value2: " << value2 << std::endl;

    // Add 5 to value2 by passing value2 and function 'add5' to function 'func'
    double value3 = func(value2, &add5);
    std::cout << "Value3: " << value3 << std::endl;
}

如果您编译并运行此代码,输出应该如下所示:
Value1: 10.75
Value2: 12.75
Value3: 17.75

但是,如果你尝试将模板函数 (add2) 传递给 func,你会收到类似于 error: no matching function for call to 'func' 的错误信息。C++ 不允许你将模板函数传递给其他函数,但是 C++ 允许你将特定的模板函数实例传递给其他函数。如果你替换掉

double value3 = func(value2, &add5);

使用

double value3 = func(value2, &add2<double>);

main() 的最后,这将实现所需的结果。
Value1: 10.75
Value2: 12.75
Value3: 14.75

很遗憾,目前C++不允许您将通用模板函数传递给其他函数。


0
我能想到的最接近的方法是将函数模板包装在类包装器中以实现类似的效果(Live):
#include <iostream>
using namespace std;

struct Fn
{
    template <class Arg>
    static void fn() {cout << "Fn::fn()\n";}
};

struct Gn
{
    template <class Arg>
    static void fn() { cout << "Gn::fn()\n"; }
};

template<class Arg, class F>
void mainFn(F dummy) {
    F::template fn<Arg>(); 
}

int main()
{
    mainFn<int>(Fn{});
    mainFn<int>(Gn{});
}

输出

Fn::fn()
Gn::fn()

0

我只是想为其他可能有特殊情况的人提供我所能达到的最接近我想要的东西。

正如Nicol Bolas已经说过的那样,这只能通过类型推导实现。因此,如果您可以控制所接收的函数,您实际上可以使用它:

template<class C>
void fn(C* ignore) {}

template<class Fn, class Arg>
void mainFn(Fn fn) {
    Arg* ignore = nullptr;
    fn(ignore);
}

这将移除类型参数并利用类型推断来查找类型。

在我的情况下,这是不够的,因为RedFog提到我的findType函数实际上在看似相同的调用中返回不同的类型。


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