更改函数指针的签名并调用它以忽略返回类型是否安全?
Is it safe to change a function pointers signature and call it to ignore the return type?
在我们的代码库中,我们使用(例如;std::function<void()>
)。有时我们想要绑定一个具有不同签名的函数到回调,这可以使用bind来完成。这对于不同的函数参数来说是没问题的,但是试图绑定一个返回一些东西的函数到一个期望返回void的回调是行不通的,见这里。
我们找到的最简单的解决方案是将绑定函数的签名强制转换为具有相同参数但返回类型为void的函数:
#include <functional>
#include <iostream>
int ReturnInt()
{
std::cout << "ReturnInt" << std::endl;
return 5;
}
struct Struct
{
int ReturnInt()
{
std::cout << "Test::Func" << std::endl;
return 30;
}
};
template<typename ReturnType, typename ... ArgumentTypes>
auto IgnoreResult(ReturnType (*i_Func)(ArgumentTypes ...))
-> void (*)(ArgumentTypes ...)
{
return reinterpret_cast<void (*)(ArgumentTypes ...)>(i_Func);
}
template<typename ReturnType, typename ClassType, typename ... ArgumentTypes>
auto IgnoreResult(ReturnType (ClassType::*i_Func)(ArgumentTypes ...))
-> void (ClassType::*)(ArgumentTypes ...)
{
return reinterpret_cast<void (ClassType::*)(ArgumentTypes ...)>(i_Func);
}
int main(int argc, char **argv)
{
std::function<void ()> BoundType;
Struct instance;
BoundType = std::bind(IgnoreResult(&Struct::ReturnInt), &instance);
BoundType();
BoundType = std::bind(IgnoreResult(&ReturnInt));
BoundType();
return 0;
}
这已经在Visual Studio 2013 11月CTP, cygwin clang 3.4.2和cygwin gcc 4.8.3上进行了测试,并在所有平台上工作,但调用已转换为不同函数签名的函数指针是未定义的行为。我知道某些调用约定可能会破坏它,但据我所知,从Microsoft调用约定来看,返回类型是通过寄存器传递的,而不是通过堆栈传递的。我们也从不指定不同的调用约定,始终使用默认值。
假设gcc、clang和Microsoft编译器没有改变它们的行为,在绑定回调时,使用这个方法来忽略函数的返回类型是安全的吗?
是安全的改变一个函数指针的签名和调用它忽略返回类型?
不,它不是,参见c++标准,章节§5.2.10 [expr. reinterpretation .cast]
通过指向与函数定义中使用的类型不同的函数类型(8.3.5)的指针调用函数的效果是未定义的。
即使在特定的编译器/平台上看起来可以工作,也不能保证它真的可以工作(隐藏的副作用,堆栈损坏…)。 您应该考虑一种新的设计,首先不需要这种强制转换(没有更多的上下文很难回答)
你可以使用lambdas:
#include <functional>
#include <iostream>
int ReturnInt()
{
std::cout << "ReturnInt" << std::endl;
return 5;
}
struct Struct
{
int ReturnInt()
{
std::cout << "Test::Func" << std::endl;
return 30;
}
};
int main(int argc, char **argv)
{
std::function<void ()> BoundType;
Struct instance;
BoundType = [&instance] { instance.ReturnInt(); };
BoundType();
BoundType = [] { ReturnInt(); };
BoundType();
return 0;
}
您可以使用类似于libsigc++2中使用的方法(通过sigc::hide_return())来解决这个问题。
- 函数作为模板参数,是否对返回类型强制约束
- 检查函数返回类型是否与STL容器类型值相同
- 当返回类型声明为 ListNode 时,我们是否可以返回 false<T>*
- C++方法是否可以根据传递给构造函数的参数具有不同的返回类型?
- 通过引用传递参数;函数返回类型是否必须为 VOID?
- 是否创建具有不同返回类型的lambda
- 如果函数是在类的主体中定义的,我是否需要在成员函数的返回类型中指定 typename?
- decltype(auto) 是否使尾随返回类型过时?
- clang 拒绝具有尾随 decltype 返回类型的模板调用是否正确,具体取决于其重载之一?
- C++中的函数是否有任何默认返回类型
- 是否可以将一种函数类型转换为另一种采用相同参数但返回类型不同的函数类型
- 是否可以在尾随返回类型语法中直接使用参数值(不是其类型,而是值本身)
- 是否可以为类设置自定义返回类型
- 是否可以将“自动”关键字用作函数指针声明中使用初始化的返回类型
- 在C++中使用指针或引用作为函数的返回类型是否有经验法则
- 表达式的返回类型是否始终与操作数类型匹配
- 函数返回类型是否会影响重载的选择
- 更改函数指针的签名并调用它以忽略返回类型是否安全?
- 如果我想使类不可复制,"operator="返回类型是否重要?
- C++流插入运算符的返回类型是否需要是std::ostream