Search code examples
c++11iteratorconstantsshared-ptrmove

Why moving a shared_ptr is allowed in a const_iterator?


Moving a shared_ptr will set the moved shared_ptr to nullptr so why it is allowed to do this in a const_iterator ?

std::vector<std::shared_ptr<std::string>> sharedPtrVector;

sharedPtrVector.push_back(std::shared_ptr<std::string>(new std::string("test")));

for (std::vector<std::shared_ptr<std::string>>::const_iterator it = sharedPtrVector.begin(); it != sharedPtrVector.end(); ++it) {
    // Not allowed if const_iterator
    //*it = nullptr;

    // Not allowed if const_iterator
    //*static_cast<std::shared_ptr<std::string> *>(&*it) = nullptr;

    // Allowed even if const_iterator
    std::shared_ptr<std::string> test(std::move(*it));
}

sharedPtrVector is in an undefined state after that.


Solution

  • As discussed in the comments, std::move doesn't actually perform the move, it simply casts the iterator to an rvalue-reference so it is ready to be moved from. In the case of const std::shared_ptr<T>& it will cast to a const std::shared_Ptr<T>&& which is not accepted by the std::shared_ptr<T> move constructor so it will use the copy constructor instead.

    This can be confirmed by checking if the shared_ptr<T> pointed to by the const_iterator is empty afterwards:

    std::vector<std::shared_ptr<std::string>> sharedPtrVector;
    
    sharedPtrVector.emplace_back(std::make_shared<std::string>("test"));
    
    for (auto it = sharedPtrVector.cbegin(); it != sharedPtrVector.cend(); ++it) {
        std::shared_ptr<std::string> test(std::move(*it));
        if (*it)
            std::cout << "*it is not empty\n";
    }