2
votes

Is it valid to transfer nodes between two boost::intrusive::slist<boost::intrusive::cache_last<true>> objects? Something like the following

auto one = boost::intrusive::slist<Node, boost::intrusive::cache_last<true>>{};
auto two = boost::intrusive::slist<Node, boost::intrusive::cache_last<true>>{};

auto node = std::make_unique<Node>();
one.push_back(*node);

auto& front = one.front();
one.pop_front();
two.push_back(front);

I'm getting a segmentation fault, and an assertion failure with boost version 1.70.0 https://wandbox.org/permlink/nWHakTYUiVBGKH6I. How can I fix this?


Note: I can't allocate a new node and copy the old one because I am using an intrusive list to have control over when and where the allocation happens.

2
Sorry I deleted my answer. You are dereferencing when you push_back. I think sth happens in the dtor. I got it working but can't delete the same problem unique ptr has: wandbox.org/permlink/q5pBdd0JBdJN5ewDOblivion
@oblivion Sorry, I forgot to mention, I need to transfer the nodes. In particular I want to avoid allocating a new node for the purposes of transfering a node from one to another.Curious
you may use a ref or a shared_ptr.Oblivion

2 Answers

0
votes

It seems to be the purpose of the splice method:

Effects: Transfers all the elements of list x to this list, before the the element pointed by it. No destructors or copy constructors are called.

two.splice(two.end(), one, one.begin());

Fully working demo:

#include <iostream>
#include <string>
#include <boost/intrusive/slist.hpp>

struct Node : public boost::intrusive::slist_base_hook<>
{
    int         i;
    std::string s;

    Node(int i, std::string s) : i(i), s(std::move(s)) {}
};

using NodeList = boost::intrusive::slist<Node, boost::intrusive::cache_last<true>>;

int main()
{
    NodeList one;
    NodeList two;

    auto show = [&](auto text){
        std::cout <<text <<"\n  one\n";
        for (auto & item : one) { std::cout <<"    " <<item.i <<' ' <<item.s <<'\n'; }
        std::cout <<"  two\n";
        for (auto & item : two) { std::cout <<"    " <<item.i <<' ' <<item.s <<'\n'; }
    };


    one.push_back(*new Node(42, "hello"));
    show("before splice");

    two.splice(two.end(), one, one.begin());
    show("after splice");

    one.clear_and_dispose([](Node * ptr){ delete ptr; });
    two.clear_and_dispose([](Node * ptr){ delete ptr; });
    return 0;
}

When run, will write:

before splice
  one
    42 hello
  two
after splice
  one
  two
    42 hello
0
votes

You are deleting the node when it's still in a container -- the safe link mode checks for this and will assert.

If you delete the container first (reorder your object construction), or if you remove the node from the container before cleaning things up, there is no assertion.

e.g.

auto node = std::make_unique<Node>();
auto one = boost::intrusive::slist<Node, boost::intrusive::cache_last<true>>{};
auto two = boost::intrusive::slist<Node, boost::intrusive::cache_last<true>>{};

one.push_back(*node);

auto& front = one.front();
one.pop_front();
two.push_back(front);