| Document number: | P0357R3 | |
|---|---|---|
| Date: | 2018-11-07 | |
| Project: | Programming Language C++, Library Working Group | |
| Reply-to: | Tomasz Kamiński <tomaszkam at gmail dot com> | |
| Stephan T. Lavavej <stl at microsoft dot com> | ||
| Alisdair Meredith <ameredith1 at bloomberg dot net> | 
reference_wrapper for incomplete typesThis paper proposes a change in reference_wrapper's specification to
   support incomplete types.
operator() of reference_wrapper, so it is now const qualified.reference_wrapper and operator() member.std::reference_wrapper is a utility class created to
   allow references to be used in interfaces that were designed to pass
   objects by value. However the design of the standard component has a
   major drawback, when compared to the alternative solutions based on
   the use of the raw pointers of boost version of this component: the
   referenced type is required to be complete. As a consequence, depending 
   on the context use of reference_wrapper may increase compilation
   time by adding a new definition, or even be impossible in the case when the 
   definition of the class is not available to the programmer.
Moreover std::reference_wrapper specializations are
   recognized by standard factory functions, like: std::make_pair,
   std::make_tuple or std::bind which allow
   the programmer to create pairs of references by use of:
auto p = std::make_pair(std::ref(t), std::ref(u));
Use of this feature, not only avoids cumbersome specification of the type, but also eliminates the possibility of encountering dangling reference problems that it may introduce. For example in the case of the following definition:
std::pair<std::string const&, int> p("test", 10);
every use of p.first leads to undefined behaviour caused by
   reading a dangling reference. Despite all of the above advantages, programmers
   are still forced to use pair<T&, U&>, when at least
   one of the types T or U is incomplete.
Furthermore this problem is not addressed by inclusion of the
   P0091: Template argument deduction for class templates,
   as implicit deduction guides synthesized from pair and tuple
   constructors do not deduce reference types.
Supporting incomplete types in reference_wrapper was impossible until
   recent removal of result_type and related typedefs introduced by
   P0619: Reviewing Deprecated Facilities of C++17 for C++20,
   as to provide them, implementations were required to check the template parameter for their presence.
The feature proposed in this paper conflicts with support for the old
   function binding protocol and vendors will no longer be allowed to provide required
   typedefs in their std::reference_wrapper implementations.
is_transparentIn C++14, another protocol based on the presence of the is_transparent
   nested type was introduced, to indicate that a given functor enables heterogeneous lookup
   for associative container. As in the case of result_type implementation of
   this protocol in exact form for reference_wrapper<T> would reintroduce 
   requirement of completeness of T template parameter.
Despite the fact that support for incomplete types and heterogeneous container lookup
   in reference_wrapper may look incompatible, there is the possibility to 
   provide both of them, via an alternative design that relies on a metafunction instead of
   a nested type, as proposed in 
   P0046R1: Change is_transparent to metafunction (Revision 1).
This proposal depends on the removal of the result_type and related typedefs for C++20.
Nothing depends on this proposal.
The proposed wording changes refer to N4778 (C++ Working Draft, 2018-10-08).
At the end of the section 23.14.5 Class template reference_wrapper [refwrap]:
The template parameter
Tofreference_wrappermay be an incomplete type.
Apply following changes to paragraph 23.14.5.4 reference_wrapper invocation [refwrap.invoke]:
template <class... ArgTypes> result_of_t<T&(ArgTypes&&... )> operator()(ArgTypes&&... args) const;
- Mandates:
Tis a complete type.- Returns:
INVOKE(get(), std::forward<ArgTypes>(args)...). ([func.require] 20.12.2).
At the beginning of the paragraph 23.14.4.5 reference_wrapper helper functions [refwrap.helpers]:
The template parameter
Tof the followingrefandcreffunction templates may be an incomplete type.
For the purposes of SG10, we recommend the macro name __cpp_lib_reference_wrapper
with value 20YYMM representing publication date, to be defined in the
<functional> header. The intent is to allow reuse of the same macro to
indicate presence of the original feature from C++11 standard.
Usage example:
template<typename T, typename U>
auto my_tie(T& t, U & u)
{
#if __cpp_lib_reference_wrapper >= 20YYMM
  return std::make_pair(std::ref(t), std::ref(u));
#else
  return std::pair<T&, U&>(t, u);
#endif
}
Without requirement to conditionally support result_type and related typedefs, straightforward implementation
   provides support for incomplete types.
template<typename T>
class reference_wrapper
{
  T* ptr;
    
public:
  using type = T;  
     
  reference_wrapper(T& val) noexcept
     : ptr(std::addressof(val))
  {}
  reference_wrapper(T&&) = delete;
  
  T& get() const noexcept { return *ptr; }
  operator T&() const noexcept{ return *ptr; }
  template<typename... Args>
  auto operator()(Args&&... args) const
    -> std::result_of_t<T&(Args...)>
  { return std::invoke(*ptr, std::forward<Args>(args)...); }
};
Careful reader may notice, that the  operator() requires template parameter T to be a complete type,
   and this requirement is not only limited to definition of the function, but also its declaration, that uses 
   std::result_of_t<T&(Args...)> to specify return type. 
   However, call operator is an template function member of the class and its declaration will not be instantiated during the
   instantiation of enclosing reference_wrapper specialization, as the Args template parameter pack 
   are not know at this point. As a consequence the user is allowed to use an object of reference_wrapper<T>
   with T being an incomplete type, unless it is actually called.
Special thanks and recognition goes to Sabre (http://www.sabre.com) for supporting the production of this proposal, and for sponsoring Tomasz Kamiński's trip to the Oulu for WG21 meeting.