问题
How can I set a function pointer depending on some condition to functions with different signature?
Example:
short int A()
{
return 0;
}
long int B()
{
return 0;
}
void main()
{
std::function<short int()> f = A;
f();
if(true)
{
//error
f = B;
}
}
How can use the same function pointer for two functions with different signature? Is it possible?
If is not, there is an efficient way to call the appropriate function depending on behavior instead of use a variable and split the whole code with if statements?
EDIT / EXPANSION ("2nd case")
#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;
}
}
I have a compilation error on a similar case using the Uint16
and Uint32
variable of SDL 2
library, using std::mem_fn
the compiler give me this error (relative to my code, but it's implemented in a way like the above example):
error: no match for ‘operator=’ (operand types are ‘std::_Mem_fn<short unsigned int (IO::File::*)()>’ and ‘std::_Mem_fn<unsigned int (IO::File::*)()>’)
To resolve this compilation error, I forced both the function to return a int
type.
Is there a better way?
Or I did something wrong?
回答1:
The comments already say that clang accepts the code as is, and I can now say that GCC 4.8.4 and GCC 4.9.2 both accept it as well, after fixing void main()
to say int main()
.
This use of std::function
is perfectly valid. The C++11 standard says:
20.8.11.2 Class template function [func.wrap.func]
function& operator=(const function&); function& operator=(function&&); function& operator=(nullptr_t);
There is no template assignment operator here, so assignment of B
could only construct a new temporary function<short int()>
object, and move-assign from that. To determine whether the construction of that temporary is possible:
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 beCopyConstructible
.f
shall beCallable
(20.8.11.2) for argument typesArgTypes
and return typeR
. The copy constructor and destructor ofA
shall not throw exceptions.20.8.11.2 Class template function [func.wrap.func]
2 A callable object
f
of typeF
isCallable
for argument typesArgTypes
and return typeR
if the expressionINVOKE
(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)
asINVOKE
(f, t1, t2, ..., tN)
implicitly converted toR
.1 Define
INVOKE
(f, t1, t2, ..., tN)
as follows:
- ... (all related to pointer-to-member types)
f(t1, t2, ..., tN)
in all other cases.
In short, this means that std::function<short int()>
can be used with any function that can be called with no arguments, and which has a return type that can be implicitly converted to short
. long
clearly can be implicitly converted to short
, so there is no problem whatsoever.
If your compiler's library doesn't accept it, and you cannot upgrade to a more recent version, one alternative is to try boost::function
instead.
Aaron McDaid points out lambdas as another alternative: if your library's std::function
is lacking, you can write
std::function<short int()> f = A;
f = []() -> short int { return B(); };
but if you take this route, you can take it a step further and avoid std::function
altogether:
short int (*f)() = A;
f = []() -> short int { return B(); };
This works because lambas that don't capture anything are implicitly convertible to a pointer-to-function type that matches the lambda's arguments and return type. Effectively, it's short for writing
short int B_wrapper() { return B(); }
...
f = B_wrapper;
Note: the conversion from long
to short
may lose data. If you want to avoid that, you can use std::function<long int()>
or long int (*)()
instead.
回答2:
No, you can't do that in a statically typed language unless your types all have a common super type, and C++ doesn't have that for primitives. You would need to box them into an object, then have the function return the object.
However, if you did that, you may as well just keep an object pointer around and use that instead of a function pointer, especially since it's going to make it easier to actually do something useful with the result without doing casts all over the place.
For example, in a calculator I wrote in Java, I wanted to work with BigInteger fractions as much as possible to preserve precision, but fallback to doubles for operations that returned irrational numbers. I created a Result
interface, with BigFractionResult
and DoubleResult
implementations. The UI code would call things like Result sum = firstOperand.add(otherOperand)
and didn't have to care which implementation of add
it was using.
回答3:
The cleanest option that comes to mind is templates:
#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;
}
In ideone.com this outputs:
4 bytes with value 0
4 bytes with value 0
2 bytes with value 0
Hopefully this is what you needed.
If for some reason you really need to pass an actual function around, I would recommend looking into std::function and trying to write some template code using that.
来源:https://stackoverflow.com/questions/28167166/how-can-set-different-function-signature-to-the-same-function-pointer