更改函数指针的签名并调用它以忽略返回类型是否安全?

Is it safe to change a function pointers signature and call it to ignore the return type?

本文关键字:返回类型 是否 安全 调用 指针 函数      更新时间:2023-10-16

在我们的代码库中,我们使用(例如;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月CTPcygwin clang 3.4.2cygwin gcc 4.8.3上进行了测试,并在所有平台上工作,但调用已转换为不同函数签名的函数指针是未定义的行为。我知道某些调用约定可能会破坏它,但据我所知,从Microsoft调用约定来看,返回类型是通过寄存器传递的,而不是通过堆栈传递的。我们也从不指定不同的调用约定,始终使用默认值。

假设gccclangMicrosoft编译器没有改变它们的行为,在绑定回调时,使用这个方法来忽略函数的返回类型是安全的吗?

是安全的改变一个函数指针的签名和调用它忽略返回类型?

不,它不是,参见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())来解决这个问题。