Make std::function tolerate semantically non-CopyConstructible objects
authorJonathan Wakely <jwakely@redhat.com>
Wed, 9 May 2018 13:28:11 +0000 (14:28 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 9 May 2018 13:28:11 +0000 (14:28 +0100)
To satisfy the CopyConstructible requirement a callable object stored in
a std::function must behave the same when copied from a const or
non-const source. If copying a non-const object doesn't produce an
equivalent copy then the behaviour is undefined. But we can make our
std::function more tolerant of such objects by ensuring we always copy
from a const lvalue.

Additionally use an if constexpr statement in the _M_get_pointer
function to avoid unnecessary instantiations in the discarded branch.

* include/bits/std_function.h (_Base_manager::_M_get_pointer):
Use constexpr if in C++17 mode.
(_Base_manager::_M_clone(_Any_data&, const _Any_data&, true_type)):
Copy from const object.
* testsuite/20_util/function/cons/non_copyconstructible.cc: New.

From-SVN: r260080

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/std_function.h
libstdc++-v3/testsuite/20_util/function/cons/non_copyconstructible.cc [new file with mode: 0644]

index 25430fd9e512ba581f7eef459424c60bbd974634..5d6d6ebe175d2a874c8f7c32c6ca0aa1223cbaab 100644 (file)
@@ -1,3 +1,11 @@
+2018-05-09  Jonathan Wakely  <jwakely@redhat.com>
+
+       * include/bits/std_function.h (_Base_manager::_M_get_pointer):
+       Use constexpr if in C++17 mode.
+       (_Base_manager::_M_clone(_Any_data&, const _Any_data&, true_type)):
+       Copy from const object.
+       * testsuite/20_util/function/cons/non_copyconstructible.cc: New.
+
 2018-05-08  François Dumont  <fdumont@gcc.gnu.org>
 
        * src/c++11/debug.cc [_GLIBCXX_HAVE_EXECINFO_H]: Include execinfo.h.
index 96261355a9d61fb3e83412b5a0e58d849902f4de..ee94d1ca81e32eae62842808c3f9d620631de1fb 100644 (file)
@@ -131,8 +131,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   class _Function_base
   {
   public:
-    static const std::size_t _M_max_size = sizeof(_Nocopy_types);
-    static const std::size_t _M_max_align = __alignof__(_Nocopy_types);
+    static const size_t _M_max_size = sizeof(_Nocopy_types);
+    static const size_t _M_max_align = __alignof__(_Nocopy_types);
 
     template<typename _Functor>
       class _Base_manager
@@ -150,10 +150,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        static _Functor*
        _M_get_pointer(const _Any_data& __source)
        {
-         const _Functor* __ptr =
-           __stored_locally? std::__addressof(__source._M_access<_Functor>())
-           /* have stored a pointer */ : __source._M_access<_Functor*>();
-         return const_cast<_Functor*>(__ptr);
+         if _GLIBCXX17_CONSTEXPR (__stored_locally)
+           {
+             const _Functor& __f = __source._M_access<_Functor>();
+             return const_cast<_Functor*>(std::__addressof(__f));
+           }
+         else // have stored a pointer
+           return __source._M_access<_Functor*>();
        }
 
        // Clone a location-invariant function object that fits within
@@ -170,7 +173,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        _M_clone(_Any_data& __dest, const _Any_data& __source, false_type)
        {
          __dest._M_access<_Functor*>() =
-           new _Functor(*__source._M_access<_Functor*>());
+           new _Functor(*__source._M_access<const _Functor*>());
        }
 
        // Destroying a location-invariant object may still require
diff --git a/libstdc++-v3/testsuite/20_util/function/cons/non_copyconstructible.cc b/libstdc++-v3/testsuite/20_util/function/cons/non_copyconstructible.cc
new file mode 100644 (file)
index 0000000..d2a9992
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright (C) 2018 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+
+#include <functional>
+
+// This type is not CopyConstructible because copying a non-const lvalue
+// will call the throwing constructor.
+struct A
+{
+  A() = default;
+  A(const A&) { } // not trivial, so allocated on the heap by std::function
+  A(A&) { throw 1; }
+  void operator()() const { }
+};
+
+int main()
+{
+  const A a{};
+  // Undefined, because std::function requires CopyConstructible:
+  std::function<void()> f(a);
+  // This will throw if the object is copied as non-const:
+  std::function<void()> g(f);
+}