据我了解,typedef
不能用于重载,但是如果我需要使用一些不同类型作为函数指针的参数怎么办?
如何使其具有以下功能?
{
public:
typedef void (*InitFunc)(float x);
typedef void (*InitFunc)(int a, char b); //Needs to be added
virtual void initialize(InitFunc init) = 0;
};
修改:
我不能使用C ++ 17,所以不能使用variant
答案 0 :(得分:0)
如前所述,最简单的方法是联合,尽管不是很安全的类型和C ++-y。这是一个继承的示例,因为您注释了要继承的内容。
typedef void (*FloatInit)(float x);
typedef void (*IntCharInit)(int a, char b);
union InitFn {
FloatInit fi;
IntCharInit ici;
};
struct Foo {
void initialize(InitFn) = 0;
};
struct FloatFoo: public Foo {
void initialize(InitFn f) override {
f.fi(42.0f);
}
};
void test(float) {}
// ...
auto x = FloatFoo{};
x.initialize(InitFn{test});
正如其他评论者所提到的,您可以使用std::variant
来增强类型安全性并摆脱手动的联合定义:
typedef void (*FloatInit)(float x);
typedef void (*IntCharInit)(int a, char b);
typedef std::variant<FloatInit, IntCharInit> InitFn;
struct Foo {
void initialize(InitFn) = 0;
};
struct FloatFoo: public Foo {
void initialize(InitFn f) override {
std::get<FloatInit>(f)(42.0f);
}
};
void test(float) {}
// ...
auto x = FloatFoo{};
x.initialize(InitFn{test});
答案 1 :(得分:0)
一种解决方案是改为创建一个简单的包装器类模板,以使编译器根据需要自动生成实例化。如果始终保证init
是非成员函数(并且通过扩展,它是实际函数而不是函子/ lambda),则这相对简单。
// Quick-and-dirty transparent callable wrapper, to serve as overloadable "type alias".
template<typename>
class InitFunc;
template<typename Ret, typename... Params>
class InitFunc<Ret(*)(Params...)> {
public:
// Supply component types if needed.
// Tuple used for params, for convenience.
using return_type = Ret;
using param_types = std::tuple<Params...>;
using func_type = Ret(Params...);
using func_ptr_type = func_type*;
using func_ref_type = func_type&;
// Create from pointer or reference.
constexpr InitFunc(func_ptr_type p = nullptr) : ptr(p) {}
constexpr InitFunc(func_ref_type r) : ptr(&r) {}
// Transparent invocation.
// Deduces argument types instead of relying on Params, to allow for perfect forwarding.
template<typename... Ts>
constexpr return_type operator()(Ts&&... ts) { return ptr(std::forward<Ts>(ts)...); }
// Convert back to original type if necessary.
operator func_ptr_type() { return ptr; }
operator func_ref_type() { return *ptr; }
private:
// Actual function pointer.
func_ptr_type ptr;
};
// And a nice, clean creator, which can be renamed as necessary.
template<typename Init>
constexpr auto make(Init func) { return InitFunc<Init>(func); }
这会创建一个漂亮的小包装,可以轻松对其进行完全优化,will compile as long as C++14 support is available。
请注意,您绝对需要最低限度的C ++ 11编译器(或可变参数模板,右值引用,完美转发和constexpr
支持),并且需要修改make()
为C ++ 14之前的编译器提供尾随返回类型。我相信这与C ++ 11 constexpr
兼容,但我不确定100%。
如果您希望InitFunc
能够接受指向成员函数的指针/引用(包括函子和lambda),则需要提供其他版本以将其隔离为非成员“功能”,并可能将其绑定到类实例。在这种情况下,也许值得研究std::bind()
,尽管我不确定它是否有任何开销。
在这种情况下,我建议将成员类型拆分为一个基类,以减少您需要重复的代码量。
// Quick-and-dirty transparent callable wrapper, to serve as overloadable "type alias".
template<typename>
class InitFunc;
// Supply component types if needed.
// Tuple used for params, for convenience.
// Using actual function type as a base, similar to std::function.
template<typename Ret, typename... Params>
class InitFunc<Ret(Params...)> {
public:
using return_type = Ret;
using param_types = std::tuple<Params...>;
using func_type = Ret(Params...);
using func_ptr_type = func_type*;
using func_ref_type = func_type&;
};
// Non-member functions.
// As member types are now dependent types, we qualify them and use `typename`.
// Yes, it looks just as silly as you think it does.
template<typename Ret, typename... Params>
class InitFunc<Ret(*)(Params...)> : public InitFunc<Ret(Params...)> {
// Actual function pointer.
typename InitFunc::func_ptr_type ptr;
public:
// Create from pointer or reference.
constexpr InitFunc(typename InitFunc::func_ptr_type p = nullptr) : ptr(p) {}
constexpr InitFunc(typename InitFunc::func_ref_type r) : ptr(&r) {}
// Transparent invocation.
// Deduces argument types instead of relying on Params, to allow for perfect forwarding.
template<typename... Ts>
constexpr typename InitFunc::return_type operator()(Ts&&... ts) { return ptr(std::forward<Ts>(ts)...); }
// Convert back to original type if necessary.
operator typename InitFunc::func_ptr_type() { return ptr; }
operator typename InitFunc::func_ref_type() { return *ptr; }
};
// See ecatmur's http://stackoverflow.com/a/13359520/5386374 for how to accomodate member functions.
// ...
// Non-member function make() is unaffected.
// An overload will likely be needed for member functions.
template<typename Init>
auto make(Init func) { return InitFunc<Init>(func); }
尽管我们派生的专业知识很笨拙,但依我所知,任何依赖InitFunc
的代码都不应看到对其API的任何更改;前一个示例将work just fine if we swap to this new InitFunc
,并且在重新编译后再明智不过。
请注意,尽管如此,它将更改ABI,因此,为该版本简化的InitFunc
编译的任何代码都需要为此版本重新编译。