我有一些这样定义的功能:
ParentClass* fun1();
ParentClass* fun2();
ParentClass* fun3(bool inp=false);
ChildClass* fun4();
ChildClass* fun5(int a=1, int b=3);
我想将它们放入如下所示的某种数组中:
void* (*arr[5])() = {
(void* (*)())fun1,
(void* (*)())fun2,
(void* (*)())fun3,
(void* (*)())fun4,
(void* (*)())fun5
}
现在,我想简单地将此函数数组用作
for(int i=0; i<5; i++)
someFunction(arr[i]());
现在我在这里意识到问题是void* (*arr[5])()
,但是鉴于我只想使用这些函数而不提供参数,所以我希望所有这些都属于同一数组。
但是,这些是非常C风格的方法。使用C ++中的模板,还有更好的方法吗?
答案 0 :(得分:42)
是否具有C风格,您拥有的都是不确定的行为。使用lambda:
void (*arr[5])() = {
[] { fun1(); },
[] { fun2(); },
[] { fun3(); },
[] { fun4(); },
[] { fun5(); }
};
可以,因为它们通过函数的正确类型执行调用,并且它们本身可以转换为void (*)()
。
转发返回值保持足够简单,因为lambda提供了转换的上下文。在您的情况下,由于ChildClass
应该继承自ParentClass
,因此隐式转换就足够了:
ParentClass *(*arr[5])() = {
[]() -> ParentClass * { return fun1(); },
[]() -> ParentClass * { return fun2(); },
[]() -> ParentClass * { return fun3(); },
[]() -> ParentClass * { return fun4(); },
[]() -> ParentClass * { return fun5(); }
};
答案 1 :(得分:16)
但是考虑到我只想使用函数而不提供参数
它根本不能那样工作。您是否曾经想过,那么当您将函数声明放在标头中时,为什么必须将默认参数写入标头中而又不能将其放在实现源文件的定义中?
这是因为默认参数实际上并未“嵌入”到函数中,而是由编译器用来在调用位置使用那些参数来扩展函数调用,在这些位置处省略了这些参数。 (EDIT:另外,正如@Aconcagua在评论中所敏锐地观察到的那样,由于默认参数通常被定义为标头函数声明的一部分,因此对默认值的任何更改都需要对包含这些标头的任何编译单元进行完全重新编译,ergo函数声明,以使更改真正生效!)
虽然完全有可能做一些非常奇怪的类型转换疯狂来构造这样的函数指针数组,但最终您必须将其转换回原始函数调用签名,以免调用未定义的行为。
如果您需要绑定函数指针以及某种类型的默认参数集(可以抽象化调用),则可以提供这些参数,而外部可以提供一个多态接口。因此,您将拥有一个std::vector<function_binder>
或function_binder[]
,其中函数绑定器有一个operator()
调用了该函数。
但是,当您首先进行绑定时,可以将其绑定到匿名函数(即lambdas)中。在lambda实例化时,默认参数已绑定。
std::vector<void(*)()> fvec = {
[]{ func0(); },
[]{ func1(); },
[]{ func2(); },
}
答案 2 :(得分:9)
您可以使用std::bind
std::function<ParentClass *(void)> arr[5] = {
std::bind(&fun1),
std::bind(&fun2),
std::bind(&fun3, false),
std::bind(&fun4),
std::bind(&fun5, 1, 3)
};
现在你可以做
for(int i=0; i<5; i++)
arr[i]();
您必须确保所有函数的每个函数参数都已绑定。
这对成员函数也很好。您只需要将对象引用(例如this
)绑定为第一个参数即可。
答案 3 :(得分:3)
c++20解决方案:
func localized(_ key: String) -> String {
let uiTestBundle = Bundle(for: AClassFromYourUITests.self)
return NSLocalizedString(key, bundle: uiTestBundle, comment: "")
}
这是很多样板,但可以帮助我们:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
template<auto f, class R, class...Args>
struct explicit_function_caster {
using Sig=R(Args...);
using pSig=Sig*;
constexpr operator pSig()const {
return [](Args...args)->R {
return static_cast<R>(f(std::forward<Args>(args)...));
};
}
};
template<auto f>
struct overload_storer_t {
template<class R, class...Args>
constexpr (*operator R() const)(Args...) const {
return explicit_function_caster<f, R, Args...>{};
}
template<class...Args>
auto operator()(Args&&...args)
RETURNS( f( std::forward<Args>(args)... ) )
};
template<auto f>
overload_storer_t<f> generate_overloads={};
#define OVERLOADS_OF(...) \
generate_overloads< \
[](auto&&...args) \
RETURNS( __VA_ARGS__( decltype(args)(args)... ) ) \
>
基本上ParentClass* (*arr[5])() = {
OVERLOADS_OF(fun1),
OVERLOADS_OF(fun2),
OVERLOADS_OF(fun3),
OVERLOADS_OF(fun4),
OVERLOADS_OF(fun5)
};
void (*arr2[5])() = {
OVERLOADS_OF(fun1),
OVERLOADS_OF(fun2),
OVERLOADS_OF(fun3),
OVERLOADS_OF(fun4),
OVERLOADS_OF(fun5)
};
接受一个generate_overloads<x>
可调用对象constexpr
,并让您在编译时将其强制转换为指向任何兼容签名的函数的指针,并(几乎)使用任何签名
同时,x
将函数名称转换为OVERLOADS_OF
对象,该对象对该函数名称进行重载解析。我在这里使用它是因为constexpr
作为函数指针并不知道其默认参数,但是在重载解析时就知道了。
在这种情况下,编写玩具lambda来完成这项工作要容易得多;这只是为了自动编写那些玩具lambda以获取任意兼容签名的尝试。
答案 4 :(得分:1)
由于您已经用C ++ 14标记了问题,因此不应使用函数指针!
对于C ++ 14,您应该更喜欢std::function
和lambda。
您也不应该使用C样式数组,而只能使用std::array
和/或std::vector
。
也要避免使用原始指针,请使用std::unique_ptr
和std::shared_ptr
。
最简单,最好的解决方法是:
std::array<std::function<ParentClass*()>,5> arr {
[]() { return fun1(); },
[]() { return fun2(); },
[]() { return fun3(true); },
[]() { return fun4(); },
[]() { return fun5(7, 9); }
};
为什么不像@Quentin回答中那样简单的指针数组?他使用了lambda,但不能使用可以绑定任何东西的lambda(如果需要)。