(注意:如果这感觉像是 XY 问题,请滚动到分隔符下方,了解我是如何得出这个问题的)
我正在寻找一种方法来存储指向成员函数的指针(不同类型的)并比较它们是否相等。我需要存储一个从指针到成员函数到任意对象的映射,然后搜索这个映射。它不必是关联容器,线性搜索就可以了。另请注意,指针仅用作映射键,它们永远不会被取消引用。
我目前的方法是这样的:在构建映射时,我reinterpret_cast
将传入的指向成员的指针指向一种众所周知的类型 ( void (MyClass::*)()
) 并将其插入到映射中。像这样的东西(为简洁起见省略了错误检查):
template <class R, class... A)
void CallChecker::insert(R (MyClass::*key)(A...), Object value)
{
mapping.push_back(std::make_pair(reinterpret_cast<void (MyClass::*)()>(key), value));
}
然后在查找时,我执行相同的强制转换并按相等搜索:
template <class R, class... A)
Object CallChecker::retrieve(R (MyClass::*key)(A...)) const
{
auto k = reinterpret_cast<void (MyClass::*)()>(key);
auto it = std::find_if(begin(mapping), end(mapping), [k](auto x) { return x.first == k; });
return it->second;
}
但是,我不确定这是否会一直有效。虽然我相信它不会产生假阴性(两个相等的指针被报告为不同的),但我担心它可能会产生假阴性(原本不同类型的两个指针在转换为“普通”类型时可以比较相等)。所以我的问题是,是这样吗?或者我可以安全地使用这样的比较吗?
我知道我在这里危险地靠近 UB 领土。但是,我不介意使用标准未定义但已知可在 gcc 和 MSVC(我的两个目标编译器)中工作的行为的解决方案。
所以,问题是:普通类型中的比较安全吗?或者我最好将存储的指针转换为传入类型以进行比较(如下所示):
template <class R, class... A)
Object CallChecker::retrieve(R (MyClass::*key)(A...)) const
{
auto it = std::find_if(begin(mapping), end(mapping), [key](auto x) { return reinterpret_cast<R (MyClass::*)(A...)>(x.first) == key; });
return it->second;
}
或者这些都不会在实践中起作用,我不走运?
鉴于我的实际任务和加深我对语言的理解,我对指向成员的指针的上述属性感兴趣。尽管如此,出于完整的感觉(如果有人知道更好的方法),这就是我如何得出最初的问题。
我正在构建一个实用程序框架来帮助对Qt4信号进行单元测试(测试是否发出了正确的信号)。我的想法是创建一个类CallChecker
来存储插槽的验证器(包装std::function
对象),并能够运行它们。然后,测试将创建一个由此派生的类;该类将定义运行相应验证器的插槽。这是使用的想法(简化):
class MyTester : public QObject, public CallChecker
{
Q_OBJECT
public slots:
void slot1(int i, char c) { CallChecker::checkCall(&MyTester::slot1, i, c); }
void slot2(bool b) { CallChecker::checkCall(&MyTester::slot2, b); }
};
void testRunner()
{
MyTester t;
connectToTestedSignals(t);
t.addCheck(&MyTester::slot1, [](int i, char c) { return i == 7; });
}
我有一个工作实现(ideone 上的 gcc),其中CallChecker
使用一std::vector
对,指向成员的指针强制转换为通用函数类型。在对编译器标志/vmg
(
如果您可以提出比通过指向成员的指针查找更好的解决方案,我会很高兴听到它。我的目标是在实现测试槽的类中易于使用:我真的希望这些槽是简单的单行。使用插槽签名的文本表示(Qt 内部使用的)并不是一个真正的选择,因为它太容易出现拼写错误。