函数指针的现代 C++ 替代品

modern c++ alternative to function pointers

到目前为止我一直在使用函数指针,就像c++中的这种格式。我现在确实有一些用途,我想知道 c++11/14 中是否引入了其他任何替代方案。

#include <iostream>
using namespace std;

void sayHello();
void someFunction(void f());

int main() {

    someFunction(sayHello);
    return 0;
}
void sayHello(){
    std::cout<<"\n Hello World";
}
void someFunction(void f()){
    f();
} 

我确实看过这个 question 但无法理解与传统使用函数指针相比​​有什么优势。另外我想问一下,使用函数指针有什么问题(不推荐)吗,因为我从未见过有人使用它们。或任何其他替代方案。

您提到的问题建议 std::function,但在与 std::bind 结合时没有强调(或根本没有提及)它的价值。

你的例子是最简单的,但假设你有一个

std::function<void (int, int)> f ;

函数指针可以做或多或少相同的事情。但是假设你需要一个函数 g(int),它是 f,第二个参数绑定到 0。使用函数指针你不能做太多,使用 std::function 你可以这样做:

std::function<void(int)> g = std::bind(f, _1, 0) ;

I did take a look at this question but couldn't understand any advantages over traditional use of function pointers. Also I would like to ask , is there anything wrong (not recommended) thing with using function pointers since I never see anyone using them.

  • 普通 "global" 函数通常没有 t/can 状态。虽然在函数式编程范例的遍历过程中拥有状态不一定好,但有时当状态与已更改的内容正交相关时(例如启发式),状态可能会派上用场。这里仿函数(或函数对象)有优势。

  • 普通函数不能很好地组合(创建低级函数的高级函数。

  • 普通函数不允许动态绑定附加参数。

  • 有时普通函数可以代替 lambda,反之亦然,具体取决于上下文。人们通常不想仅仅因为在 "container traversal".

  • 期间有一些非常 local/specific 的要求而编写特殊函数

作为传统函数指针的替代方案,C++11 引入了 template alias which combined with variadic templates 可以简化函数指针语法。下面是如何创建 "template" 函数指针的示例:

template <typename R, typename ...ARGS> using function = R(*)(ARGS...);

可以这样使用:

void foo()                { ... }
int bar(int)              { ... }
double baz(double, float) { ... }

int main()
{
    function<void>                  f1 = foo;
    function<int, int>              f2 = bar;
    function<double, double, float> f3 = baz;

    f1(); f2({}); f3({}, {});
    return 0;
}

此外,它可以巧妙地处理函数重载:

void overloaded(int)      { std::cout << "int version\n"; }
void overloaded(double)   { std::cout << "double version\n"; }

int main()
{
    function<void, int>    f4 = overloaded;
    function<void, double> f5 = overloaded;

    f4({}); // int version
    f5({}); // double version
    return 0;
}

并且可以用作声明函数指针参数的一种非常简洁的方法:

void callCallback(function<int, int> callback, int value)
{
    std::cout << "Calling\n";
    std::cout << "v: " << callback(value) << '\n';
    std::cout << "Called\n";
}

int main()
{
    function<int, int> f2 = bar;
    callCallback(f2, {});
    return 0;
}

此模板别名可以用作 std::function 的替代品,既没有缺点也没有优点 (good explanation here)。

Live demo

简而言之,我认为模板别名与可变参数模板相结合是 raw 函数指针的一个很好、漂亮、简洁和现代的 C++ 替代品(这个别名仍然是函数指针毕竟)但是 std::function 是好的、漂亮的、简洁的和现代的 C++ 以及需要考虑的良好优势。坚持使用函数指针(或别名)或选择 std::function 取决于您的实现需要。

Also I would like to ask , is there anything wrong (not recommended) thing with using function pointers since I never see anyone using them.

。函数指针是非常糟糕的东西。首先,它们不支持泛型——所以你不能使用一个函数指针,比如说,对任何 T 使用 std::vector<T>。其次,他们不支持绑定状态,所以如果在未来的任何时候,任何人想要引用其他状态,他们就完蛋了。这尤其糟糕,因为这包括成员函数的 this

在 C++11 中有两种获取函数的方法。第一种是使用模板。第二种是使用std::function.

模板看起来像这样:

template<typename T> void func(F f) {
    f();
}

这里的主要优点是它接受任何类型的函数object,包括函数指针、lambda、仿函数、bind-result等等,并且 F 可以有任意数量的函数调用重载任何签名,包括模板,它可以具有任何绑定状态的任何大小。所以它 super-duper 灵活。它也是最有效的,因为编译器可以内联运算符并直接在 object.

中传递状态
int main() {
    int x = 5;
    func([=] { std::cout << x; });
}

这里的主要缺点是模板的常见缺点 - 它不适用于虚函数并且必须在 header.

中定义

另一种方法是std::functionstd::function 有许多相同的优点——它可以是任何大小,可以绑定到任何状态,并且可以是任何可调用的,但有一些折衷。主要是,签名在类型定义时是固定的,所以你不能有一些 yet-to-be-known Tstd::function<void(std::vector<T>)>,并且可能还涉及一些动态的 indirection/allocation(如果你不能 SBO)。这样做的好处是,由于 std::function 是一个真正的具体类型,您可以像任何其他类型一样传递它 object,因此它可以用作虚函数参数和诸如此类的东西。

基本上,函数指针非常有限,不能真正任何有趣的事情,并且使API非常不灵活。他们可恶的语法是大海中的小便,用模板别名减少它很有趣但毫无意义。