<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title>Relaxing the structured bindings customization point finding rules</title>

	<style>
	p {text-align:justify}
	li {text-align:justify}
	blockquote.note
	{
		background-color:#E0E0E0;
		padding-left: 15px;
		padding-right: 15px;
		padding-top: 1px;
		padding-bottom: 1px;
	}
	ins {color:#00A000}
	del {color:#A00000}
	</style>
</head>
<body>

<address align=right>
Document number: P0961R0
<br/>
Audience: EWG
<br/>
<br/>
<a href="mailto:ville.voutilainen@gmail.com">Ville Voutilainen</a><br/>
2018-02-11<br/>
</address>
<hr/>
<h1 align=center>Relaxing the structured bindings customization point finding rules</h1>

<h2>Abstract</h2>
<p>
  This paper proposes relaxing the rules for finding a customization
  point for structured bindings; that is, just because a
  a member get() that is not a template is found, that should not disable
  an ADL customization point. This allows writing ADL get()
  for types that happen to have a get() member function in their
  class hierarchy. Examples of such types are smart pointers, future-like
  types, and user wrappers that happen to inherit from such types.
</p>

<p>
  The proposal in this paper is that, if a member get()
  is found, it must be a member template to be used with a structured
  binding. If a get() that is not a templateis found, that member is not
  used, and the lookup continues to try and find an ADL get&lt;&gt;().
</p>

<h2>Rationale</h2>

<p>
  It would seem perfectly reasonable to be able to adapt
  types that have a non-template member get() to be used with
  structured bindings. Currently, structured bindings will
  try to use a member get() even when it can never be valid,
  because it's not a get&lt;&gt;().
</p>


<h2>Rumination</h2>

<p>
  So, first of all, are the current rules really wrong? It certainly
  seems reasonable to allow using a member get&lt;&gt;(). I'm not suggesting
  disallowing that. However, as with the range-for customization
  point lookup, we seem to lose useful functionality when we
  have the rules commit to a member as soon as it remotely
  smells like it may have been intended to be structured bindings
  customization point.
</p>

<p>
  However, that rationale relies on the assumption that the user
  somehow intended to opt in to structured bindings, failed to do
  so correctly, and is now surprised that some namespace-scope
  get&lt;&gt; was chosen. The problem here is that
  if the user instead intended to write such a namespace-scope
  get&lt;&gt;(), and can't change the presence of an 'get'
  in his class hierarchy, that's just impossible to do.
</p>

<p>
  While it was a nice idea to avoid funny results in the examples
  discussed during C+17 standardization, I think it's an unfortunate consequence
  that we now prevent perfectly valid use cases written by programmers
  who know what they are doing. We shouldn't base our rules on
  overt worrying about things that could go wrong, but rather
  allow useful programming techniques to be applied, and rely
  on tools to hold the hand of a programmer. That is, we used
  to have a principle of trusting the programmer rather than
  molly-coddling him.
</p>

<p>
  Is this a significant problem? Probably not, not something
  that a whole lot of programmers run into. However, the problem
  seems easy to fix, with not much downside. We have more cases
  where customization point lookup rules are draconian and prevent
  reasonable code from working; I have written another paper that deals with
  a customization point lookup problem with a range-for loop. These
  problems can be worked around with adapter types, but I'd rather
  have less draconian rules.
</p>

<h2>An example of code that doesn't work</h2>

<p>
<pre>
<code>
#include &lt;memory&gt;
#include &lt;tuple&gt;
#include &lt;string&gt;

struct X : private std::shared_ptr&lt;int&gt;
{
    std::string fun_payload;
};

template&lt;int N&gt; std::string&amp; get(X&amp; x) {if constexpr(N==0) return x.fun_payload;}

namespace std {
    template&lt;&gt; class tuple_size&lt;X&gt; : public std::integral_constant&lt;int, 1&gt; {};
    template&lt;&gt; class tuple_element&lt;0, X&gt; {public: using type = std::string;};
}

int main()
{
    X x;
    auto&amp; [y] = x;
}
</code>
</pre>
</p>
<p>
  I find it very unreasonable that that code is ill-formed.
  I could use some wrapper as a work-around, but in public
  inheritance cases, I would lose the standard conversion
  to a base. In addition, that requires users to use the wrapper
  in part of their client code, but the bare non-wrapped type in other parts.
</p>
<p>
  Side question: if I change the structured binding declaration to
  auto [y] = x;, gcc and clang complain that they can't bind
  the (xvalue) argument to the lvalue-reference parameter. I can add an overload
  for const (but I don't want const here, I want a modifiable
  object), and I can add an overload for an rvalue (but this seems
  odd, because I don't want to move). I don't understand
  why the argument to get&lt;&gt; becomes an xvalue when all I seemed
  to use was an lvalue? This may work fine for using structured
  bindings with function return values, but is extremely surprising
  for binding a named variable. The relevant wording is
  in [dcl.struct.bind]/3:
  <blockquote>
    In either case, e is an lvalue if the type of the entity e is an lvalue reference and an xvalue otherwise.
  </blockquote>
  I grok that what is passed to get&lt;&gt; is a copy of x. What I
  find user-hostile is that defining a get&lt;&gt; that takes a X&amp; and
  a get&lt;&gt;  that takes a const X&amp; is not sufficient.
</p>

<h2>Wording</h2>

<p>
  In [dcl.struct.bind]/3, edit as follows:
</p>

<p>
<blockquote>
Otherwise, if the qualified-id std::tuple_size&lt;E&gt; names a complete type, the expression std::tuple_size&lt;E&gt;::value shall be a well-formed integral constant expression and the number of elements in the
identifier-list shall be equal to the value of that expression. The unqualified-id get is looked up in the scope of
E by class member access lookup (6.4.5), and if that finds at least one declaration<ins> that is a template whose first parameter is a non-type parameter
of integral type</ins>, the initializer is e.get&lt;i&gt;().
Otherwise, the initializer is get&lt;i&gt;(e), where get is looked up in the associated namespaces (6.4.2). In either
case, get&lt;i&gt; is interpreted as a template-id.
</blockquote>
</p>

</body>

</html>
