This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Resolved status.

### 2501. `std::function` requires POCMA/POCCA

Section: 20.14.17.3 [func.wrap.func] Status: Resolved Submitter: David Krauss Opened: 2015-05-20 Last modified: 2020-09-06

Priority: 3

View all other issues in [func.wrap.func].

View all issues with Resolved status.

Discussion:

The idea behind `propagate_on_container_move_assignment` is that you can keep an allocator attached to a container. But it's not really designed to work with polymorphism, which introduces the condition where the current allocator is non-POCMA and the RHS of assignment, being POCMA, wants to replace it. If function were to respect the literal meaning, any would-be attached allocator is at the mercy of every assignment operation. So, `std::function` is inherently POCMA, and passing a non-POCMA allocator should be ill-formed.

The other alternative, and the status quo, is to ignore POCMA and assume it is true. This seems just dangerous enough to outlaw. It is, in theory, possible to properly support POCMA as far as I can see, albeit with difficulty and brittle results. It would require function to keep a throwing move constructor, which otherwise can be `noexcept`.

The same applies to `propagate_on_container_copy_assignment`. This presents more difficulty because `std::allocator` does not set this to true. Perhaps it should. For function to respect this would require inspecting the POCCA of the source allocator, slicing the target from the erasure of the source, slicing the allocation from the erasure of the destination, and performing a copy with the destination's allocator with the source's target. This comes out of the blue for the destination allocator, which might not support the new type anyway. Theoretically possible, but brittle and not very practical. Again, current implementations quietly ignore the issue but this isn't very clean.

The following code example is intended to demonstrate the issue here:

>     #include <functional>
>     #include <iostream>
>     #include <vector>

>     template <typename T>
>     struct diag_alloc
>     {
>       std::string name;

>       T* allocate(std::size_t n) const
>       {
>         std::cout << '+' << name << '\n';
>         return static_cast<T*>(::operator new(n * sizeof(T)));
>       }

>       void deallocate(T* p, std::size_t) const
>       {
>         std::cout << '-' << name << '\n';
>         return ::operator delete(p);
>       }

>       template <typename U>
>       operator diag_alloc<U>() const { return {name}; }

>       friend bool operator==(const diag_alloc& a, const diag_alloc& b)
>       { return a.name == b.name; }

>       friend bool operator!=(const diag_alloc& a, const diag_alloc& b)
>       { return a.name != b.name; }

>       typedef T value_type;

>       template <typename U>
>       struct rebind { typedef diag_alloc<U> other; };
>     };

>     int main() {
>       std::cout << "VECTOR\n";
>       std::vector<int, diag_alloc<int>> foo({1, 2}, {"foo"}); // +foo
>       std::vector<int, diag_alloc<int>> bar({3, 4}, {"bar"}); // +bar

>       std::cout << "move\n";
>       foo = std::move(bar); // no message

>       std::cout << "more foo\n";
>       foo.reserve(40); // +foo -foo
>       std::cout << "more bar\n";
>       bar.reserve(40); // +bar -bar

>       std::cout << "\nFUNCTION\n";
>       int bigdata[100];
>       auto bigfun = [bigdata]{};
>       typedef decltype(bigfun) ft;
>       std::cout << "make fizz\n";
>       std::function<void()> fizz(std::allocator_arg, diag_alloc<ft>{"fizz"}, bigfun); // +fizz
>       std::cout << "another fizz\n";
>       std::function<void()> fizz2;
>       fizz2 = fizz; // +fizz as if POCCA
>       std::cout << "make buzz\n";
>       std::function<void()> buzz(std::allocator_arg, diag_alloc<ft>{"buzz"}, bigfun); // +buzz
>       std::cout << "move\n";
>       buzz = std::move(fizz); // -buzz as if POCMA

>       std::cout << "\nCLEANUP\n";
>     }

[2016-08, Chicago]

Tues PM: Resolved by P0302R1.

Proposed resolution:

Resolved by P0302R1.