This patch implements the C++2a proposal to allow incomplete types in std::reference_wrapper, which was previously undefined.
The change cannot be implemented for earlier standards, because prior to C++2a std::reference_wrapper has a weak result type, so must inspect the template argument to see if it defines a nested result_type member. That is deprecated (but still required) in C++17, and removed from C++2a. The removal of the base class from reference_wrapper is a potential ABI change, as it could alter the layout of a type which derives from reference_wrapper<T> and from an empty type with _Weak_result_type<T> as a base class. Previously the repeated _Weak_result_type<T> base class would have prevented the empty base-class optimization, but if reference_wrapper<T> no longer derives from it, the empty class could be placed at the same address as the reference_wrapper<T> base. In practice, the only types which derive from _Weak_result_type or from _Reference_wrapper_base_memfun or any of its base classes are non-empty types defined in libstdc++: std::reference_wrapper, std::function, and std::_Bind. As they are non-empty types, they are not eligible for EBO anyway. * include/bits/refwrap.h [__cplusplus > 201703L] (_Refwrap_base_arg1, _Refwrap_base_arg2, _Reference_wrapper_base) (_Reference_wrapper_base_memfun): Do not define for C++2a. (reference_wrapper): Do not derive from _Reference_wrapper_base_memfun for C++2a. (reference_wrapper::operator()): Add static assertion. * testsuite/20_util/reference_wrapper/incomplete.cc: New test. Tested powerpc64le-linux, committed to trunk.
commit d41fa8998e6b9760b4946df464296a2472e5fc06 Author: Jonathan Wakely <jwak...@redhat.com> Date: Fri Jan 11 22:56:01 2019 +0000 P0357R3 reference_wrapper for incomplete types This patch implements the C++2a proposal to allow incomplete types in std::reference_wrapper, which was previously undefined. The change cannot be implemented for earlier standards, because prior to C++2a std::reference_wrapper has a weak result type, so must inspect the template argument to see if it defines a nested result_type member. That is deprecated (but still required) in C++17, and removed from C++2a. The removal of the base class from reference_wrapper is a potential ABI change, as it could alter the layout of a type which derives from reference_wrapper<T> and from an empty type with _Weak_result_type<T> as a base class. Previously the repeated _Weak_result_type<T> base class would have prevented the empty base-class optimization, but if reference_wrapper<T> no longer derives from it, the empty class could be placed at the same address as the reference_wrapper<T> base. In practice, the only types which derive from _Weak_result_type or from _Reference_wrapper_base_memfun or any of its base classes are non-empty types defined in libstdc++: std::reference_wrapper, std::function, and std::_Bind. As they are non-empty types, they are not eligible for EBO anyway. * include/bits/refwrap.h [__cplusplus > 201703L] (_Refwrap_base_arg1, _Refwrap_base_arg2, _Reference_wrapper_base) (_Reference_wrapper_base_memfun): Do not define for C++2a. (reference_wrapper): Do not derive from _Reference_wrapper_base_memfun for C++2a. (reference_wrapper::operator()): Add static assertion. * testsuite/20_util/reference_wrapper/incomplete.cc: New test. diff --git a/libstdc++-v3/include/bits/refwrap.h b/libstdc++-v3/include/bits/refwrap.h index 5299b212510..6b4335a22ac 100644 --- a/libstdc++-v3/include/bits/refwrap.h +++ b/libstdc++-v3/include/bits/refwrap.h @@ -175,6 +175,7 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type) : _Weak_result_type_memfun<typename remove_cv<_Functor>::type> { }; +#if __cplusplus <= 201703L // Detect nested argument_type. template<typename _Tp, typename = __void_t<>> struct _Refwrap_base_arg1 @@ -279,6 +280,7 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type) { using result_type = typename _Mem_fn_traits<_MemFunPtr>::__result_type; }; +#endif // ! C++20 /** * @brief Primary class template for reference_wrapper. @@ -287,7 +289,11 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type) */ template<typename _Tp> class reference_wrapper +#if __cplusplus <= 201703L + // In C++20 std::reference_wrapper<T> allows T to be incomplete, + // so checking for nested types could result in ODR violations. : public _Reference_wrapper_base_memfun<typename remove_cv<_Tp>::type> +#endif { _Tp* _M_data; @@ -327,6 +333,9 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type) typename result_of<_Tp&(_Args&&...)>::type operator()(_Args&&... __args) const { +#if __cplusplus > 201703L + static_assert(sizeof(type), "type must be complete"); +#endif return std::__invoke(get(), std::forward<_Args>(__args)...); } }; diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/incomplete.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/incomplete.cc new file mode 100644 index 00000000000..6fce8d9e304 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/incomplete.cc @@ -0,0 +1,36 @@ +// Copyright (C) 2019 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-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +// P0357R3 reference_wrapper for incomplete types + +#include <functional> + +struct Incomplete; + +template class std::reference_wrapper<Incomplete>; + +Incomplete& f(); + +std::reference_wrapper<Incomplete> r = f(); +static_assert( std::is_same_v<decltype(r)::type, Incomplete> ); +static_assert( std::is_same_v<decltype(r.get()), Incomplete&> ); + +std::reference_wrapper r2 = f(); +static_assert( std::is_same_v<decltype(r), decltype(r2)> );