我有一个指向未知类型变量的指针void* p
,还有一个装有std::set<MyClass*> c
指针的容器MyClass
。是否有某种方法可以找出c
是否包含p
(即是否包含一个指向与p
相同的内存地址的指针)而无需手动循环访问{{1 }},这不会导致未定义的行为吗? (请注意,如果我在c
中找不到p
,就不会取消引用。)
此外,我假设如果c
指向与p
不相关的数据类型的变量,则将MyClass*
强制转换为p
会导致未定义的行为,但是也许不是这样吗?
答案 0 :(得分:1)
您可以安全地将void*
强制转换为另一个指针,但不要取消引用它。在std::set<T*>
中查找指针不会取消引用该指针(除非您指定一个这样做的自定义比较谓词)。
根据C ++标准,目前看来,仅加载无效的指针是未定义的行为。但是,这是使用segmented addressing(x86实模式)的硬件体系结构的一种规定,其中加载指针会将其值的一部分加载到段寄存器中,这可能会导致硬件陷阱。在具有平面内存模型的现代体系结构中,此限制不适用,加载任何指针值的定义都很好,因为这只是加载到通用CPU寄存器中。
类似的东西:
MyClass* find(std::set<MyClass*> const& c, void* p) {
auto found = c.find(static_cast<MyClass*>(p));
return found != c.end() ? *found : nullptr;
}
您可以像这样使用它:
std::set<MyClass*> c;
void* p = ...;
if(MyClass* q = find(c, p))
// p is found and is q
答案 1 :(得分:1)
我假设如果[..]
,将p
强制转换为MyClass*
会导致未定义的行为
从理论上讲,它可能会导致UB。
但是可能应该在实践中起作用(UB的乔伊)。
是否有某种方法可以找出c是否包含p [..],而无需手动循环访问c中的元素。
std::find_if
或std::binary_search
可以与适当的谓词一起使用以在线性时间内找到它(std::set::iterator
不是随机迭代器,因此“伪造” binary_search
的复杂性)。
如果您可以将容器更改为:
std::set<MyClass*, std::less<void>>
,那么您可以安全地使用std::set::find
感谢透明的比较器。
对std::vector<MyClass*>
进行了排序,那么您可能会以正确的复杂性使用std::binary_search
。
答案 2 :(得分:0)
在实践中,您应该能够做到(请参阅其他分析工具和评论),但是您需要注意一些陷阱。
如果您有从另一个类(例如OtherClass
)和MyClass
派生的类,则需要确保p
指向MyClass
部分而不是{{ 1}}。虽然您不会有未定义的行为,但是找不到该项目。
OtherClass
在这种情况下,您不会在集合中找到class OtherClass { int someData; };
class WontWork : public OtherClass, public MyClass { };
auto *w = new WontWork();
c.insert(w);
void *p = w;
,因为p
的值不同于插入到集合中的值(在隐式转换为p
之后)。
要使代码正常工作,您需要执行以下操作来使MyClass *
指向p
部分:
MyClass
或
void *p = static_cast<MyClass *>(w);
但是,然后,必须确保不要在代码中的其他位置使用MyClass *mc = w;
p = mc;
或p
类型的OtherClass
。
如果可能的话,最好通过修正声明来完全避免使用WontWork
。