Hi,
I observe more and more types with "reference-like" semantics are being added to the standard. Starting from
string_view (and our old friends bitset::reference and vector<bool>::reference), we've got optional<T&> and function_ref. span and mdspan are also to an extent a reference-like type, though it can be argued that they are more of pointer-like types.I've long hated these reference-like, or so-called "proxy" types because they are all broken, in the sense that they behave very differently from the native references. The language simply disallows a "proper" reference-like type to be written. There are two reasons I know of why this is impossible (there are maybe more):
1. Native references get lifetime-extended, but never with proxies.
2. It is impossible to make a proxy type to behave in the same way as the native references in the context of type deduction.
There are certain situations where these result in inconvenience and/or lifetime bugs.
- For instance, the user of a function in a library must know if the return type is reference-like or value-like. Some may argue the user must know the exact return type anyway, but there are many legitimate situations where that is not the case. Most notable is the famous expression templates technique. Since avoiding unnecessary temporaries is why this technique is used in the first place, return types are naturally "opaque/hidden" reference-like types. The consequence is that the user must know what is the correct corresponding value-like type and must not use
auto to initialize a variable from these functions/operators returning expression templates.- Similar fiasco happens in general for type-erasure. Basically, if you want to make your type-erased type to follow the proper value-semantics, you have to either create a separate pairing type with reference-semantics (like
function_ref) or just accept unnecessary allocations from happening all the time when a function expecting type-erased arguments got called with non-erased types (which is one of the main motivation behind string_view). Now, when you have a value-type/reference-type pair (or a triple or quadruple or maybe more to include const references and rvalue references), a similar problem arises with auto. The user of an API must know if the function they are calling is returning a complete object (aka value type) or a reference to a cached result stored inside a class, if they want to avoid unnecessary allocation. Ideally, the user would just use auto if they want a copy, or to use auto const& or auto&& if they just want to refer to it transiently and then discard. That simple rule very easily breaks down when type-erasure is involved.- These are especially problematic in the context of generic code. Now quite often it's not only hard but also simply impossible to know this distinction of value/reference types. The usual expectation is that we get a copy if we use
T and a reference if we use T&/T const&/T&&, but that (otherwise perfectly reasonable) assumption easily breaks down when proxies are involved.As reference-like types are being increasingly common, I am wondering what is the status of any attempts for making them true 1st class citizen. Does anyone have any idea?
https://redd.it/1ts2vai
@r_cpp