现代c ++替代函数指针

时间:2015-06-10 06:43:29

标签: c++ pointers c++11 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,但是无法理解任何优于传统使用函数指针的优点。另外我想问一下,使用函数指针有什么不对(不推荐)的事情因为我从未见过有人使用它们。或任何其他替代方案。

4 个答案:

答案 0 :(得分:11)

你提到的问题建议使用std :: function,但当与std :: bind结合使用时,它的值并不强调(或提及)。

您的示例是最简单的,但假设您有

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

函数指针可以或多或少地执行相同的操作。但是假设你需要一个函数g(int),它是第二个参数绑定为0的函数。使用函数指针你可以做很多事情,使用std :: function你可以这样做:

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

答案 1 :(得分:4)

作为传统函数指针的替代方法,C ++ 11引入template aliasvariadic templates结合可以简化函数指针sintax。下面是一个如何创建&#34;模板&#34;函数指针:

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

简而言之,我认为模板别名与可变参数模板相结合是原始函数指针的一个优秀,漂亮,整洁和现代的C ++替代品(这个别名毕竟仍然是函数指针)但是{ {1}}是好的,漂亮的,整洁的和现代的C ++,同时考虑到了很好的优势。坚持使用函数指针(或别名)或选择std::function取决于您的实现需求。

答案 2 :(得分:3)

  

我确实看过这个问题,但无法理解   优于传统使用函数指针的优点。我也愿意   喜欢问,是否有任何错误(不推荐)的事情   使用函数指针,因为我从未见过有人使用它们。

  • 正常&#34;全球&#34;功能通常不具备状态。虽然在函数式编程范例的遍历过程中获得状态并不一定好,但有时当状态与已经改变的东西正交时(例如,启发式),状态可能会派上用场。仿函数(或函数对象)具有优势。

  • 正常功能不能很好地构建(创建更低级别功能的更高级功能。

  • 正常功能不允许动态绑定其他参数。

  • 有时,正常功能可以替代lambdas,反之亦然,具体取决于具体情况。通常,一个人不想写一个特殊的功能只是因为你在&#34;容器遍历&#34;期间有一些非常本地/特定的要求。

答案 3 :(得分:1)

  

另外我想问一下,有什么不对(不推荐)   使用函数指针的事情因为我从未见过任何人使用过   它们。

的。函数指针是可怕的,可怕的东西。首先,它们不支持通用 - 所以你不能使用一个函数指针,比如说std::vector<T>用于任何T。其次,他们不支持有约束力的国家,所以如果在将来的任何时候,任何人都希望引用其他国家,他们就完全搞砸了。这尤其糟糕,因为这包括this成员函数。

在C ++ 11中使用函数有两种方法。首先是使用模板。第二种是使用std :: function。

模板有点像这样:

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

这里的主要优点是它接受任何类型的函数对象,包括函数指针,lambda,functor,bind-result等等,并且F可以具有任意数量的函数调用重载以及任何签名,包括模板,以及它可以具有任何约束状态的任何大小。所以它超级灵活。它也是最高效的,因为编译器可以内联运算符并直接在对象中传递状态。

int main() {
    int x = 5;
    func([=] { std::cout << x; });
}

这里的主要缺点是模板的通常缺点 - 它不适用于虚拟功能,必须在标题中定义。

另一种方法是std::functionstd::function具有许多相同的优点 - 它可以是任何大小,绑定到任何状态,并且可以是任何可调用的,但是可以交换一些。主要是,签名在类型定义时固定,因此对于某些尚未知道的std::function<void(std::vector<T>)>,您不能拥有T,并且可能还存在一些动态间接/分配(如果你不能SBO)。这样做的好处是,因为std::function是一个真正的具体类型,你可以像任何其他对象一样传递它,所以它可以用作虚函数参数等等。

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