如何将不同的函数签名设置为同一个函数指针?
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
库的 Uint16
和 Uint32
变量时遇到类似情况的编译错误,使用 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
接口,带有 BigFractionResult
和 DoubleResult
实现。 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;
注意:从long
到short
的转换可能会丢失数据。如果你想避免这种情况,你可以使用 std::function<long int()>
或 long int (*)()
代替。
如何根据某些条件将函数指针设置为具有不同签名的函数?
示例:
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
库的 Uint16
和 Uint32
变量时遇到类似情况的编译错误,使用 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
接口,带有 BigFractionResult
和 DoubleResult
实现。 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 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.
简而言之,这意味着 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;
注意:从long
到short
的转换可能会丢失数据。如果你想避免这种情况,你可以使用 std::function<long int()>
或 long int (*)()
代替。