键入成员函数指针的“正确”方法是什么?

问题描述 投票:4回答:1

我正在编写一个测试装置,其中涉及确保在适当的时间调用某些回调(实际上是Qt信号,但是就我的问题而言这无关紧要)。为了解决这个问题,我创建了一个帮助器类,该类记录何时将回调(信号)触发到列表中。

此列表必须能够记录which触发的回调(信号)。我也希望不需要为此专门创建一个新的枚举。我的想法是改为将信号的地址记录为类型擦除的指针,以便我可以对照信号的地址来检查记录。

为了使我自己更轻松一些,我将信号类型记录为:

template <typename Object>
class SignalType
{
public:
  SignalType() = default;
  SignalType(SignalType const&) = default;
  SignalType(SignalType&&) = default;

  template <typename R, typename... Args>
  SignalType(R (Object::*member)(Args...))
    : member{reinterpret_cast<void (Object::*)()>(member)} {}

  template <typename R, typename... Args>
  bool operator==(R (Object::*other)(Args...)) const
  { return this->member == reinterpret_cast<void (Object::*)()>(other); }

private:
  void (Object::*member)() = nullptr;
};

这从使用的角度“隐藏”了类型擦除,所以我以后可以写:

QCOMPARE(event.type, &SomeObject::someMethod);

......不需要强制转换。

但是,GCC不满意:

warning: cast between incompatible pointer to member types from ‘void (SomeObject::*)(...)’ to ‘void (SomeObject::*)()’ [-Wcast-function-type]

是否有一种方法可以使GCC满意而无需借助诊断#pragma来简单地关闭警告?是否有其他“更好”的方法来实现这种特定类型的擦除效果? (请注意,我不需要call SignalType封装的成员;我只需要能够测试是否相等即可。)


叹息。应该搜索警告消息,而不是我要执行的操作。 技术上我猜想这是Cast Between Incompatible Function Types in gcc的副本,但是只问如何摆脱警告,还不清楚代码试图完成什么。因此,为了使我在这里可以学到一些有用的信息,请着重注意是否还有其他“更清洁”的方法来实现我的目标,而不仅仅是将其作为重复内容并说“无法解决”。

c++ c++11 function-pointers member-function-pointers
1个回答
0
投票

这里是一个解决方案,其中包含一个函数指针,该函数指针可以比较两个相同类型的值,同时如果两个类型相同,还可以在运行时充当std::type_info检查。它将功能指针存储在char[]中。

#include <new>

template<typename Object>
class SignalType
{
public:
  SignalType() = default;
  SignalType(SignalType const&) = default;
  SignalType& operator=(SignalType const&) = default;

  template<typename R, typename... Args>
  SignalType(R (Object::*member)(Args...)) noexcept
    : comparator(&compare_members_from_void_ptr<R, Args...>) {
    using member_ptr_type = R(Object::*)(Args...);
    static_assert(sizeof(member_ptr_type) <= sizeof(void(Object::*)()), "Member pointer type too large?");
    static_assert(alignof(member_ptr_type) <= alignof(void(Object::*)()), "Member pointer align too large?");
    // Don't need to destruct since it has a trivial destructor
    new (member_storage) member_ptr_type(member);
  }

  bool operator==(const SignalType& other) const {
    if (!comparator) return !other.comparator;  // Check both empty
    // Same comparator implies same type
    return comparator == other.comparator && comparator(member_storage, other.member_storage);
  }
  bool operator!=(const SignalType& other) const {
    return !(*this == other);
  }

  // true if currently holding a pointer
  explicit operator bool() const {
    return comparator;
  }

  // Check if holding an `R(Object::*)(Args...)`
  template<typename R, typename... Args>
  bool is_type() const noexcept {
    return comparator && comparator == &compare_members_from_void_ptr<R, Args...>;
  }

  // Returns the held function pointer if it is of type R(Object::*)(Args...), else nullptr
  template<typename R, typename... Args>
  R(Object::* get() const noexcept)(Args...) {
    return is_type<R, Args...>() ? *static_cast<R(Object::**)(Args...)>(static_cast<void*>(member_storage)) : nullptr;
  }
private:
  alignas(void(Object::*)()) char member_storage[sizeof(void(Object::*)())];
  bool (*comparator)(const void*, const void*);

  template<typename R, typename... Args>
  static bool compare_members_from_void_ptr(const void* a, const void* b) noexcept {
    return *static_cast<R(Object::*const *)(Args...)>(a) == *static_cast<R(Object::*const *)(Args...)>(b);
  }
};
© www.soinside.com 2019 - 2024. All rights reserved.