3

在我们的代码库中,我们有使用(例如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 November CTPcygwin clang 3.4.2cygwin gcc 4.8.3进行了测试,并且适用于所有平台,但调用已转换为不同函数签名的函数指针是未定义的行为。

我知道某些调用约定可能会破坏它,但据我从Microsoft调用约定中可以看出,返回类型是通过寄存器而不是通过堆栈传递的。我们也从不指定不同的调用约定并始终使用默认值。

假设gccclangMicrosoft编译器不改变它们的行为,这是一种在绑定回调时忽略函数返回类型的安全方法吗?

4

3 回答 3

4

您可以使用 lambda:

#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;
}
于 2014-08-20T09:56:26.000 回答
4

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

不,不是,参见 C++ 标准,第 5.2.10 节 [expr.reinterpret.cast]

通过指向与函数定义中使用的类型不同的函数类型(8.3.5)的指针调用函数的效果是未定义的。

即使它看起来确实可以在特定的编译器/平台上运行,也无法保证它确实可以运行(隐藏的副作用、堆栈损坏......)。

你应该考虑一个新的设计,首先不需要这个演员(如果没有更多的上下文很难回答)

于 2014-08-20T10:20:33.047 回答
3

您可以使用类似于libsigc++2 中使用的方法(通过 sigc::hide_return())来解决您的问题。

于 2014-08-20T11:23:07.423 回答