简体   繁体   English

为什么std :: find在std :: shared_ptr上不起作用

[英]Why std::find doesn't work on std::shared_ptr

We came across something we can not explain at work, and even if we found a solution, i would like to know exactly why the first code was fishy. 我们遇到了一些我们在工作中无法解释的事情,即使我们找到了解决方案,我也想确切地知道为什么第一个代码很烂。

Here a minimal code example : 这是一个最小的代码示例:

#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>

int main() {

    std::vector<std::shared_ptr<int>> r;

    r.push_back(std::make_shared<int>(42));
    r.push_back(std::make_shared<int>(1337));
    r.push_back(std::make_shared<int>(13));
    r.push_back(std::make_shared<int>(37));

    int* s = r.back().get();

    auto it = std::find(r.begin(),r.end(),s); // 1 - compliation error
    auto it = std::find(r.begin(),r.end(),std::shared_ptr<int>(s)); // 2 - runtime error
    auto it = std::find_if(r.begin(),r.end(),[s](std::shared_ptr<int> i){
    return i.get() == s;}
    ); // 3 -works fine

    if(it == r.end())
        cout << "oups" << endl;
    else
        cout << "found" << endl;    

    return 0;
}

So what i want to know is why the find are not working. 所以我想知道的是为什么这个发现不起作用。

  1. For the first one, it seems that shared_ptr do not have a comparison operator with raw pointers , can someone explain why ? 对于第一个,似乎shared_ptr没有带有原始指针的比较运算符 ,有人可以解释为什么吗?
  2. The second one seems to be a problem of ownership, multiple delete (when my local shared_ptr goes out of scope it delete my pointer), but what i don't understand is why the runtime error is during the find execution, the double delete should happen only on the vector destruction, any thoughts ? 第二个似乎是所有权问题,多次删除(当我的本地shared_ptr超出范围时,它删除了我的指针),但是我不明白的是为什么运行时错误是在查找执行期间发生的,因此两次删除应该仅在媒介破坏上发生,有什么想法吗?

I have the working solution with the find_if so what i really want is to understand why the first two are not working, not another working solution (but if you have a more elegant one, feel free to post). 我有find_if的有效解决方案,所以我真正想要的是理解为什么前两个不起作用,而不是另一个有效解决方案(但是如果您有一个更优雅的解决方案,请随时发布)。

For the first one, it seems that shared_ptr do not have a comparison operator with raw pointers, can someone explain why ? 对于第一个,似乎shared_ptr没有带有原始指针的比较运算符,有人可以解释为什么吗?

Subjective, but I certainly don't consider it a good idea for shared pointers to be comparable to raw pointers, and I think the authors of std::shared_ptr and the standard's committee agree with that sentiment. 主观的,但我当然不认为共享指针与原始指针具有可比性是一个好主意,我认为std::shared_ptr的作者和标准委员会对此观点表示赞同。

The second one seems to be a problem of ownership, multiple delete (when my local shared_ptr goes out of scope it delete my pointer), but what i don't understand is why the runtime error is during the find execution, the double delete should happen only on the vector destruction, any thoughts ? 第二个似乎是所有权问题,多次删除(当我的本地shared_ptr超出范围时,它删除了我的指针),但是我不明白的是为什么运行时错误是在查找执行期间发生的,因此两次删除应该仅在媒介破坏上发生,有什么想法吗?

s is a pointer to an int that was allocated by make_shared as part of a block, together with the reference counting information. s是指向由make_shared作为块的一部分分配的int的指针,以及引用计数信息。 It's implementation defined how it actually was allocated, but you can be sure it was not with a simple unadorned new expression, because that would allocate a seperate int in its own memory location. 它的实现定义了它的实际分配方式,但是您可以确保它没有使用简单的未经修饰的新表达式,因为那样会在其自己的内存位置分配一个单独的int值。 ie it was not allocated in any of these ways: 也就是说,它没有通过以下任何一种方式进行分配:

p = new int;
p = new int(value);
p = new int{value};

Then you passed s to the constructor of a new shared_ptr (the shared_ptr you passed as an argument to std::find ). 然后,将s传递给新的shared_ptr的构造函数(作为参数传递给std::findshared_ptr )。 Since you didn't pass a special deleter along with the pointer, the default deleter will be used. 由于没有将特殊的删除器与指针一起传递,因此将使用默认的删除器。 The default deleter will simply call delete on the pointer. 默认的删除器将只对指针调用delete

Since the pointer was not allocated with an unadorned new expression, calling delete on it is undefined behavior. 由于没有为指针分配未经修饰的新表达式,因此对它调用delete是未定义的行为。 Since the temporary shared_ptr will be destroyed at the end of the statement, and it believes it is the sole owner of the integer, delete will be called on the integer at the end of the statement. 由于临时shared_ptr将在语句末尾销毁,并且认为它是整数的唯一所有者,因此将在语句末尾对整数调用delete This is likely the cause of your runtime error. 这可能是您的运行时错误的原因。

Try the following, easier to reason about snippet, and you will likely run into the same problem: 尝试以下操作,更容易推断出代码段,您可能会遇到相同的问题:

auto p = std::make_shared<int>(10);
delete p.get();  // This will most likely cause the same error.
                 // It is undefined behavior though, so there
                 // are no guarantees on that.

The smart pointer class template std::shared_ptr<> only supports operators for comparison against other std::shared_ptr<> objects; 智能指针类模板std::shared_ptr<>仅支持将运算符与其他std::shared_ptr<>对象进行比较; not raw pointers. 不是原始指针。 Specifically, these are supported in that case: 具体来说,在这种情况下支持以下功能:

operator==    - Equivalence
operator!=    - Negated equivalence
operator<     - Less-than
operator<=    - Less-than or equivalent
operator>     - Greater-than
operator>=    - Greater-than or equivalent

Read here for more info 在这里阅读更多信息

Regarding why in the first case, because it isn't just a question of value ; 至于为什么在第一种情况下,因为它不只是一个价值问题; its a question of equivalence. 这是一个对等的问题。 A std::shared_ptr<> cannot be considered equivalent or comparable to a raw address simply because that raw address may not be held within a shared pointer. 仅仅因为该原始地址可能保存在共享指针中,所以不能认为std::shared_ptr<>与原始地址等效或相当。 And even if the addresses are value equivalent, that doesn't mean the source of the latter came from a properly reference-counted equivalence (ie another shared pointer). 即使地址等价的,也并不意味着后者的来源就来自经过适当引用计数的等效项(即另一个共享指针)。 Interestingly, your second example exposes what happens when you try to rig that system. 有趣的是,您的第二个示例展示了尝试装配该系统时发生的情况。

Regarding the second case, constructing a shared pointer as you are will proclaim two independent shared pointers having independent ownership of the same dynamic resource. 关于第二种情况,按原样构造共享指针将声明两个具有相同动态资源独立所有权的独立共享指针。 So ask yourself, which one gets to delete it ? 因此,问问自己,哪个人可以删除它? Um... yeah. 嗯...是的 Only when you replicate the std::shared_ptr<> itself will the reference count material shared among shared pointers holding the same datum reference be properly managed, so your code in this case is just-plain wrong . 仅当您复制std::shared_ptr<>本身时,才可以正确管理在具有相同基准引用的共享指针之间共享的引用计数材料,因此在这种情况下,您的代码是普通错误的

If you want to hunt a raw address down in a collection of shared pointers, your third method is exactly how you should do it. 如果你想共享指针的集合狩猎原始地址下来,你的第三个方法正是你应该怎么做。

Edit: Why does the ownership issue in case 2 render where it does? 编辑:为什么情况2的所有权问题会在哪里呈现呢?

Ok, I did some hunting, and it turns out its a runtime-thing (at least on my implementation). 好的,我做了一些狩猎,结果发现它是一个运行时的东西(至少在我的实现中)。 I would have to check to know for sure if this behavior (of std::make_shared ) is hardened in the standard, but I doubt it). 我将必须检查以确定该行为( std::make_shared行为)是否在标准中得到了加强,但我对此表示怀疑。 The bottom line is this. 底线是这个。 These two things: 这两件事:

r.push_back(new int(42));

and

r.push_back(std::make_shared<int>(42));

can do very different things. 可以做非常不同的事情。 The former dynamically allocates a new int , then send its address off to the matching constructor for std::shared_ptr<iint> , which allocates its own shared reference data that manages referencing counting to the provided address. 前者动态分配一个新的int ,然后将其地址发送给匹配的std::shared_ptr<iint>构造函数,后者构造自己的共享引用数据,该数据用于管理对提供地址的引用计数。 Ie there are two distinct blocks of data from separate allocations. 也就是说,有来自不同分配的两个不同的数据块。

But the latter does something different. 但是后者的功能有所不同。 It allocates the object and the shared reference data in the same memory block , using placement-new for the object itself and either move-construction or copy-construction depending on what is provided/appropriate. 它使用同一对象的placement-new方法在同一内存块中分配对象共享参考数据,并根据提供的内容/适当的内容使用移动构造或复制构造。 The result is there is one memory allocation, and it holds both the reference data and the object, the latter being an offset within the allocate memory. 其结果是有一个存储器分配,它拥有两个基准数据对象,所述分配存储器内后者的偏移量。 Therefore the pointer you're sending to your shared_ptr did not come from an allocation return value. 因此,您要发送到shared_ptr的指针不是来自分配返回值。

Try the first one, and i bet you'll see you're runtime error relocate to the destruction of the vector rather then the conclusion of the find. 尝试第一个,我敢打赌,您会看到您在运行时错误中重新定位为破坏向量,而不是查找结论。

  1. bool operator ==(const std::shared_ptr<T>&, const T*) doesn't exist. bool operator ==(const std::shared_ptr<T>&, const T*)不存在。
  2. It is a bad usage of std::shared_ptr it is like you do: std::shared_ptr用法很糟糕,就像您做的那样:

     int* p = new int(42); std::shared_ptr<int> sp1(p); std::shared_ptr<int> sp2(p); // Incorrect, should be sp2(sp1) // each sp1 and sp2 will delete p at end of scope -> double delete ... 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM