使用typedef重载函数指针

时间:2019-08-04 07:50:52

标签: c++

据我了解,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

2 个答案:

答案 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编译的任何代码都需要为此版本重新编译。