到目前为止,我一直在使用函数指针,就像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,但是无法理解任何优于传统使用函数指针的优点。另外我想问一下,使用函数指针有什么不对(不推荐)的事情因为我从未见过有人使用它们。或任何其他替代方案。
答案 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 alias与variadic 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)。
简而言之,我认为模板别名与可变参数模板相结合是原始函数指针的一个优秀,漂亮,整洁和现代的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::function
。 std::function
具有许多相同的优点 - 它可以是任何大小,绑定到任何状态,并且可以是任何可调用的,但是可以交换一些。主要是,签名在类型定义时固定,因此对于某些尚未知道的std::function<void(std::vector<T>)>
,您不能拥有T
,并且可能还存在一些动态间接/分配(如果你不能SBO)。这样做的好处是,因为std::function
是一个真正的具体类型,你可以像任何其他对象一样传递它,所以它可以用作虚函数参数等等。
基本上,函数指针只是非常有限,并且不能真正做任何有趣的事情,并使API非常不灵活。他们可恶的语法在海洋中是一种小便,用模板别名减少它是搞笑但没有意义。