在 MyClass 指针的容器中找到一个 void 指针?

Find a void pointer in an container of MyClass pointers?

我有一个指向未知类型变量的指针 void* p,以及一个填充了 MyClass 指针的容器 std::set<MyClass*> c。有没有办法找出 c 是否包含 p(即它是否包含指向与 p 相同的内存地址的指针),而无需手动循环遍历 [=13] 中的元素=],这不会导致未定义的行为? (请注意,如果我在 c 中找不到它,我不会取消引用 p。)

此外,我假设如果 p 指向一个不相关但与 [=12= 相关的数据类型的变量,则将 p 转换为 MyClass* 会导致未定义的行为],但也许并非如此?

您可以将 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

在实践中,您应该能够做到(参见其他答案和评论),但您需要注意一些陷阱。

如果您有 class 派生自另一个 class(比如 OtherClass)和 MyClass,那么您需要确保 p 指向MyClass 部分而不是 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,就好像它是 OtherClassWontWork 类型一样。

如果可能,最好通过修复声明来完全避免void *

I assume that casting p to MyClass* would cause undefined behavior if [..]

它可能会导致 UB 是迂腐的正确。

但在实践中可能会奏效(UB 的乐趣)。

Is there some way to find out whether c contains p [..] without manually looping through the elements in c.

std::find_ifstd::binary_search 可以与适当的谓词一起使用以在线性时间内找到它(std::set::iterator 不是随机迭代器,因此 "falsify" binary_search 复杂度)。

如果您可以将容器更改为:

  • std::set<MyClass*, std::less<void>>,那么你可以安全地使用std::set::find 感谢透明比较器。

  • 排序 std::vector<MyClass*>,那么您可以使用具有正确复杂度的 std::binary_search