如何将不同的函数签名设置为同一个函数指针?

How can set different function signature to the same function pointer?

如何根据某些条件将函数指针设置为具有不同签名的函数?

示例:

short int A() 
{
    return 0;
}

long int B()
{
    return 0;
}

void main()
{
   std::function<short int()> f = A;
   f();
   if(true)
   {
      //error
      f = B;
   }
}

两个签名不同的函数如何使用同一个函数指针? 可能吗?

如果不是,有一种有效的方法可以根据行为调用适当的函数,而不是使用变量并用 if 语句拆分整个代​​码吗?

编辑/扩展(“第二个案例”)

#include <SDL.h>

class Obj { //whatever ...}

class A 
{
private:
    Uint16 ret16() { return SDL_ReadLE16(_pFile); }
    Uint32 ret32() { return SDL_ReadLE32(_pFile); }
    _pFile = nullptr;
public:
    Obj* func() 
    {
        Obj obj = new Obj();
        _pFile = SDL_RWFromFile("filename.bin","r"));
        auto ret = std::mem_fn(&SHPfile::ret16); 
        if(true)
        {
            ret = std::mem_fn(&SHPfile::ret32);          
        }

        //ret();
        // continue whatever
        // ....
        SDL_RWclose(_pFile);
        return *obj;
    }
}

我在使用 SDL 2 库的 Uint16Uint32 变量时遇到类似情况的编译错误,使用 std::mem_fn

编译器给我这个错误(相对于我的代码,但它的实现方式类似于上面的示例):

error: no match for ‘operator=’ (operand types are ‘std::_Mem_fn<short unsigned int (IO::File::*)()>’ and ‘std::_Mem_fn<unsigned int (IO::File::*)()>’)

为了解决这个编译错误,我将两个函数都强制为 return 和 int 类型。

有没有更好的方法?

还是我做错了什么?

想到的最简洁的选项是模板:

#include <iostream>
using namespace std;

template <typename T>
T foo() {
    return 0;
}

int main() {
    long a = foo<long>();
    cout << sizeof a << " bytes with value " << a << endl;

    int b = foo<int>();
    cout << sizeof b << " bytes with value " << b << endl;

    short c = foo<short>();
    cout << sizeof c << " bytes with value " << c << endl;

    return 0;
}

在 ideone.com 中输出:

4 bytes with value 0
4 bytes with value 0
2 bytes with value 0

希望这就是您所需要的。


如果出于某种原因您确实需要传递实际函数,我建议您查看 std::function 并尝试使用它编写一些模板代码。

不,您不能在静态类型语言中执行此操作,除非您的类型都具有通用的超类型,而 C++ 没有用于基元的类型。您需要将它们装箱到一个对象中,然后使用函数 return 对象。

但是,如果你这样做了,你也可以只保留一个对象指针并使用它而不是函数指针,特别是因为它会让你更容易对结果做一些有用的事情而不用做强制转换无处不在。

例如,在我在 Java 中编写的计算器中,我想尽可能多地使用 BigInteger 分数以保持精度,但在 returned 无理数的运算中回退到双精度.我创建了一个 Result 接口,带有 BigFractionResultDoubleResult 实现。 UI 代码会调用 Result sum = firstOperand.add(otherOperand) 之类的东西,而不必关心它使用的是 add 的哪个实现。

评论已经说 clang 按原样接受代码,我现在可以说 GCC 4.8.4 和 GCC 4.9.2 都接受它,在修复 void main() 后说 int main().

std::function 的这种用法完全有效。 C++11 标准说:

20.8.11.2 Class template function [func.wrap.func]

function& operator=(const function&);
function& operator=(function&&);
function& operator=(nullptr_t);

这里没有模板赋值运算符,所以B的赋值只能构造一个新的临时function<short int()>对象,并从中移动赋值。确定是否可以建造该临时建筑:

20.8.11.2.1 function construct/copy/destroy [func.wrap.func.con]

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

7 Requires: F shall be CopyConstructible. f shall be Callable (20.8.11.2) for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.

20.8.11.2 Class template function [func.wrap.func]

2 A callable object f of type F is Callable for argument types ArgTypes and return type R if the expression INVOKE(f, declval<ArgTypes>()..., R), considered as an unevaluated operand (Clause 5), is well formed (20.8.2).

20.8.2 Requirements [func.require]

2 Define INVOKE(f, t1, t2, ..., tN, R) as INVOKE(f, t1, t2, ..., tN) implicitly converted to R.

1 Define INVOKE(f, t1, t2, ..., tN) as follows:

  • ... (all related to pointer-to-member types)
  • f(t1, t2, ..., tN) in all other cases.

简而言之,这意味着 std::function<short int()> 可以与任何可以不带参数调用的函数一起使用,并且具有可以隐式转换为 [=43= 的 return 类型]. long明明可以隐式转换为short,所以一点问题都没有。

如果您的编译器库不接受它,并且您无法升级到更新的版本,一种替代方法是尝试 boost::function

Aaron McDaid 指出 lambda 是另一种选择:如果您的图书馆 std::function 不足,您可以编写

std::function<short int()> f = A;
f = []() -> short int { return B(); };

但如果你走这条路,你可以更进一步,完全避免 std::function:

short int (*f)() = A;
f = []() -> short int { return B(); };

这是有效的,因为不捕获任何东西的 lambas 可以隐式转换为与 lambda 的参数和 return 类型匹配的函数指针类型。实际上,它是 writing

的缩写
short int B_wrapper() { return B(); }
...
f = B_wrapper;

注意:从longshort的转换可能会丢失数据。如果你想避免这种情况,你可以使用 std::function<long int()>long int (*)() 代替。