From 793726014d4ea879950f5a7c8f7c6684d51e544b Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Thu, 27 Jul 2017 14:55:17 -0400 Subject: [PATCH] Detect std::pair non-copyability Pre-C++17, std::pair can technically have an copy constructor even though it can't actually be invoked without a compilation failure (due to the underlying types being non-copyable). Most stls, including libc++ since ~3.4, use the C++17 behaviour of not exposing an uncallable copy constructor, but FreeBSD deliberately broke their libc++ to preserve the nonsensical behaviour (https://svnweb.freebsd.org/base?view=revision&revision=261801). This updates pybind's internal `is_copy_constructible` to also detect the std::pair case under pre-C++17. This also everything (except for a couple cases in the internal version) to use the internal `is_copy_constructible` rather than `std::is_copy_constructible`. --- include/pybind11/cast.h | 19 +++++++++++++------ include/pybind11/stl_bind.h | 9 +++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 0b41f942..8c8de5b8 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -758,10 +758,17 @@ template struct is_copy_constructible : std // Specialization for types that appear to be copy constructible but also look like stl containers // (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if // so, copy constructability depends on whether the value_type is copy constructible. -template struct is_copy_constructible::value && - std::is_same::value - >> : std::is_copy_constructible {}; +template struct is_copy_constructible, + std::is_same + >::value>> : is_copy_constructible {}; + +#if !defined(PYBIND11_CPP17) +// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the +// two types aren't themselves copy constructible). +template struct is_copy_constructible> + : all_of, is_copy_constructible> {}; +#endif /// Generic type caster for objects stored on the heap template class type_caster_base : public type_caster_generic { @@ -1460,7 +1467,7 @@ class type_caster> : public move_only_holder_caster> { }; template -using type_caster_holder = conditional_t::value, +using type_caster_holder = conditional_t::value, copyable_holder_caster, move_only_holder_caster>; @@ -1525,7 +1532,7 @@ template using move_is_plain_type = satisfies_none_of struct move_always : std::false_type {}; template struct move_always, - negation>, + negation>, std::is_move_constructible, std::is_same>().operator T&()), T&> >::value>> : std::true_type {}; diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index 94117472..f16e9d22 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -66,10 +66,7 @@ template void vector_if_insertion_operato template void vector_modifiers(const Args &...) { } template -void vector_if_copy_constructible(enable_if_t< - std::is_copy_constructible::value && - std::is_copy_constructible::value, Class_> &cl) { - +void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { cl.def(init(), "Copy constructor"); } @@ -113,7 +110,7 @@ void vector_if_equal_operator(enable_if_t::value, Class_> // (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems // silly to allow deletion but not insertion, so include them here too.) template -void vector_modifiers(enable_if_t::value, Class_> &cl) { +void vector_modifiers(enable_if_t::value, Class_> &cl) { using T = typename Vector::value_type; using SizeType = typename Vector::size_type; using DiffType = typename Vector::difference_type; @@ -487,7 +484,7 @@ void map_assignment(enable_if_t void map_assignment(enable_if_t< !std::is_copy_assignable::value && - std::is_copy_constructible::value, + is_copy_constructible::value, Class_> &cl) { using KeyType = typename Map::key_type; using MappedType = typename Map::mapped_type;