Hi,
On 2018-02-14 00:23, Arthur O'Dwyer wrote:
[...]
>> std::list<~~~> local;
>> local.splice(local.begin(), tasks, tasks.begin());
>
> For reference, the above two-liner is equivalent to this
> three-liner...
> decltype(tasks) local;
> local.push_back(std::move(tasks.front()));
> tasks.pop_front();
No, it is not. In the splice() call, the value_type is neither copied
nor moved. The list node is simple re-linked. In the three-liner, the
value_type is moved, a new node is allocated, and the old one destroyed.
[...]
>> auto local = tasks.extract(tasks.begin());
>
> No, that's incorrect. In C++17, the `extract` method returns a "node
> handle", which is not at all the same thing as a list.
A node_handle gives me access to the element already (cf.
std::set::node_type::value()). My specific use-case was extracting a
single element from a list (think work-queue of a thread-pool, which is
100% of my use-cases for std::list over 15 years of professional C++
development). Right now, for ownership reasons, I need to use a local
std::list. But with node_handle, which is an owner, too, I don't need
that:
auto local = tasks.extract(tasks.begin());
do_something_with(local.value());
// at end of scope: 'local' deletes 'local.value()'
// alternatively, I can insert the node into some outgoing queue:
// out.insert(out.end(), std::move(local));
The benefit here is that the extraction and insertion of the work item
does not involve memory (de)allocations.
As for ranges: splice() is ok there, since the iterator pair
unambiguously identifies the source and the single iterator the target.
It's the one-element case where the splice() call is overly opaque.
Taking a step back, I appreciate that map::extract() was conceived as
the corresponding API to list::splice(), and that therefore,
feature-wise, list::extract() is not strictly necessary. But for me, not
having followed the standardisation process of this feature, extract()
et al were a way to allow access to nodes of node-based containers.
Splicing being just one of them. Taking a cue from Herb, let's look at
use-cases for splice() and ways to make them simpler: I showed node
extraction above, but node creation is another example:
You may want to separate node allocation from insertion into a (probably
shared) container: If you want the memory allocation outside the
critical section, what do you do? You create a std::list with one
element outside the critical section, and then splice the list into the
shared container under mutex protection.
So, there's even a use-case for a std::Container::node_type::create()
(or, rather, static container::create_node() b/c of allocators)
function!
Thanks,
Marc