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_transparent
In 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
T
ofreference_wrapper
may 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:
T
is 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
T
of the followingref
andcref
function 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.