在C++中将函数作为参数传递

9

我在C++方面有些生疏,经过一年的Python编程后转而使用C++。自然地,我希望将从Python中学到的懒惰应用到C++中。

我刚刚发现了rot13算法,并对其感到兴奋。我找到了3种实现方式,并想做一个性能测试。我还想看看修改原字符串和创建新字符串之间是否有区别。因此,我最终编写了6个函数。

第一种方法中,我使用std::map映射字符,因此我构建了一个初始化该映射的类;第二种方法中,我使用三元运算符;第三种方法中,我使用位移运算符。

现在这些函数的原型如下:

// map dependent
void Rot13::convert_inplace(string& mystr){

string Rot13::convert(const string& mystr){

// ternary operator
void bitrot_inplace(string& mystr){

string bitrot(const string&  mystr){

// bit shift
void bitshiftrot_inplace(string& mystr){

string bitshiftrot(const string& mystr){

我想构建一个函数,接受那些函数作为参数进行计算并打印结果。
所以我查看了stackoverflow上的12,然后得出以下代码。
typedef void (*vfc)(string str);

void printtime_inplace(string title, vfc func){

我尝试过这种构造方式,但这意味着我的vfc返回类型受到限制,而在我的情况下,它要么是void,要么是string,并且我需要传递类的指针。 因此,我将不得不编写3个函数来适应不同的函数,即一个用于类成员函数的函数,一个用于void返回类型的函数和一个用于string返回类型的函数。
所以我问自己,这是我真的需要使用模板,以免写三次相同的函数吗?我对模板真的不太自信,但是我应该做三个typedefs并结构化printtime函数以接受模板吗?此外,有没有办法告诉模板您只接受这些类型(即我定义的类型)?
还有一个问题,这是一个好的设计吗?或者您会建议其他设计?其他实现?

2
所以基本上你想要对不同的实现进行基准测试?看一下 std::function 作为你的基准测试函数。 - Freakyy
1
看起来 Rot13::convert_inplace 是一个类成员函数。这是正确的吗? - NathanOliver
我已经尝试过,但仍需要创建更多的自定义函数。成员函数将使用std::function和std::bind,而另一个将使用std::function,但我不太确定如何使用它们。我的目标是只编写一次printtime函数。 - Pella86
是的,我看错了。我编辑了评论。 - Pella86
一个诀窍?其实不然。我在我的库中有很多字符串函数都能做到这一点。除了使函数测试更容易推广之外,它还有其自身的好处。 - Galik
显示剩余3条评论
3个回答

15

在我看来,最简单的方法是使用模板而不是尝试编写具有特定类型的函数。

template<typename Function>
void printtime_inplace(string title, Function func)
{
    //...
    func(title);
    //...
}

现在,这将允许您使用任何“功能”内容。您可以传递常规函数、函数对象、lambda表达式、std::function等任何可调用的内容。编译器将为您生成不同的实例化版本,但就您的代码而言,您正在调用相同的函数。


1
@Pella86 你可以这样做,但是对于成员函数仍然不起作用。使用这个单一的模板函数,你不需要typedefs。它会正常工作。 - NathanOliver
@Pella86 我不明白。你是在说如果你将一个成员函数传递给 printtime_inplace 吗? - NathanOliver
在Python中,您会编写def printtime_inplace(title,funct)并接受从函数到成员函数的任何内容...现在我想在C ++中实现相同的功能。 - Pella86
1
@Pella86 好的,这样做就可以了。唯一需要注意的是,如果你想调用一个成员函数,那么你需要使用类似 my_type my_object; printtime_inplace("some sting", [&](const auto& str){ my_object.my_function(str); }); 这样的东西。 - NathanOliver
谢谢,我会试一下,Python 确实让人变懒了。 - Pella86
显示剩余2条评论

9
您可以使用 std::function 来提供这样的模板:
#include <iostream>
#include <functional>
#include <string>
#include <type_traits>

void convert_inplace(std::string& mystr){}
std::string convert(const std::string& mystr){
    return mystr;
}
void bitrot_inplace(std::string& mystr){}

template<typename ret, typename par>
using fn = std::function<ret(par)>;

template<typename ret, typename par>
void caller(fn<ret,par> f) {
    typename std::remove_reference<par>::type p;
    ret r = f(p);
}

template<typename par>
void caller(fn<void,par> f) {
    typename std::remove_reference<par>::type p;
    f(p);
}

int main() {
    auto f1 = fn<void,std::string&>(convert_inplace);
    auto f2 = fn<std::string,const std::string&>(convert);
    auto f3 = fn<void,std::string&>(bitrot_inplace);
    caller(f1);
    caller(f2);
    caller(f3);
    return 0;
}

请查看实时演示

谢谢您的回答,我还不确定我会选择std::function还是模板,但我希望有更多的选择。 - Pella86
3
std::function 的优点在于你可以使用它来调用类成员。 - user0042
@Pella86 是的,或者一个绑定的lambda函数。 - user0042
@user0042,我试图写一个没有参数的函数模板,但一直不成功。我的意思是,我想让这个 fn<void, void>(a_parameterless_function) 成为可能... 你知道怎么做吗? - JPCF

0

如果你想要一个带有可变参数的函数,这里有一个例子,它基于user0042的答案(也可以参见https://github.com/goblinhack/c-plus-plus-examples/blob/master/std_function_with_variadic_template/README.md

#include <iostream>

int my_wrapped_function (int x, const std::string y)
{
    std::cout << "SUCCESS: Hello from my_wrapped_function(x=" << x << ", y=" << y << ");" << std::endl;
    return 43;
}

void my_argument_modifier (int &x)
{
    std::cout << "SUCCESS: Hello from my_argument_modifier(x=" << x << ") => " << x + 1 << ";" << std::endl;
    x++;
}

template<typename ret, typename T, typename... Rest>
using fn = std::function<ret(T, Rest...)>;

template<typename ret, typename T, typename... Rest>
ret wrapper(fn<ret, T, Rest...> f, T t, Rest... rest)
{
    return f(t, rest...);
}

template<typename ret, typename T, typename... Rest>
ret wrapper(fn<ret, T&, Rest&...> f, T& t, Rest&... rest)
{
    return f(t, rest...);
}

int main()
{
    // Wrap a function with variable arguments
    auto f1 = fn<int,int,const std::string>(my_wrapped_function);
    auto result = wrapper(f1, 42, std::string("hello"));
    // Result should be 43: 

    // Wrap a function that modifies its arguments
    auto f2 = fn<void,int&>(my_argument_modifier);
    wrapper(f2, result);
    // Result should be 44: 

    return 0;
}

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