<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2501: std::function requires POCMA/POCCA</title>
<meta property="og:title" content="Issue 2501: std::function requires POCMA/POCCA">
<meta property="og:description" content="C++ library issue. Status: Resolved">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2501.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#Resolved">Resolved</a> status.</em></p>
<h3 id="2501"><a href="lwg-defects.html#2501">2501</a>. <code>std::function</code> requires POCMA/POCCA</h3>
<p><b>Section:</b> 22.10.17.3 <a href="https://wg21.link/func.wrap.func">[func.wrap.func]</a> <b>Status:</b> <a href="lwg-active.html#Resolved">Resolved</a>
 <b>Submitter:</b> David Krauss <b>Opened:</b> 2015-05-20 <b>Last modified:</b> 2020-09-06</p>
<p><b>Priority: </b>3
</p>
<p><b>View all other</b> <a href="lwg-index.html#func.wrap.func">issues</a> in [func.wrap.func].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Resolved">Resolved</a> status.</p>
<p><b>Discussion:</b></p>
<p>
The idea behind <code>propagate_on_container_move_assignment</code> 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, <code>std::function</code> is inherently POCMA, and passing 
a non-POCMA allocator should be ill-formed.
<p/>
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 <code>noexcept</code>.
<p/>
The same applies to <code>propagate_on_container_copy_assignment</code>. This presents more difficulty because <code>std::allocator</code> 
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.
<p/>
The following code example is intended to demonstrate the issue here:
</p>
<blockquote><pre>
#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;vector&gt;

template &lt;typename T&gt;
struct diag_alloc 
{
  std::string name;

  T* allocate(std::size_t n) const 
  {
    std::cout &lt;&lt; '+' &lt;&lt; name &lt;&lt; '\n';
    return static_cast&lt;T*&gt;(::operator new(n * sizeof(T)));
  }
  
  void deallocate(T* p, std::size_t) const 
  {
    std::cout &lt;&lt; '-' &lt;&lt; name &lt;&lt; '\n';
    return ::operator delete(p);
  }

  template &lt;typename U&gt;
  operator diag_alloc&lt;U&gt;() const { return {name}; }

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

  typedef T value_type;
  
  template &lt;typename U&gt;
  struct rebind { typedef diag_alloc&lt;U&gt; other; };
};

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

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

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

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

  std::cout &lt;&lt; "\nCLEANUP\n";
}
</pre></blockquote>

<p><i>[2016-08, Chicago]</i></p>

<p>Tues PM: Resolved by <a href="https://wg21.link/p0302r1">P0302R1</a>.</p>


<p id="res-2501"><b>Proposed resolution:</b></p>
<p>
Resolved by <a href="https://wg21.link/p0302r1">P0302R1</a>.
</p>





</body>
</html>
