<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<style type="text/css">
pre {margin-left:20pt; }
pre > i {
  font-family: "OCR A Extended", "Consolas", "Lucida Console", monospace;
  font-style:italic;
}
code > i {
  font-family: "OCR A Extended", "Consolas", "Lucida Console", monospace;
  font-style:italic;
}
pre > em {
  font-family: "OCR A Extended", "Consolas", "Lucida Console", monospace;
  font-style:italic;
}
code > em {
  font-family: "OCR A Extended", "Consolas", "Lucida Console", monospace;
  font-style:italic;
}
body { color: #000000; background-color: #FFFFFF; }
del { text-decoration: line-through; color: #8B0040; }
ins { text-decoration: underline; color: #005100; }

p.example { margin-left: 2em; }
pre.example { margin-left: 2em; }
div.example { margin-left: 2em; }

code.extract { background-color: #F5F6A2; }
pre.extract { margin-left: 2em; background-color: #F5F6A2;
  border: 1px solid #E1E28E; }

p.function { }
.attribute { margin-left: 2em; }
.attribute dt { float: left; font-style: italic;
  padding-right: 1ex; }
.attribute dd { margin-left: 0em; }

blockquote.std { color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.stddel { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5em padding-right: 0.5em; ; }

blockquote.stdins { text-decoration: underline;
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3; padding: 0.5em; }

table.header { border: 0px; border-spacing: 0;
  margin-left: 0px; font-style: normal; }

table { border: 1px solid black; border-spacing: 0px;
  margin-left: auto; margin-right: auto; }
th { text-align: left; vertical-align: top;
  padding-left: 0.4em; border: none; 
  padding-right: 0.4em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.4em; border: none;
  padding-right: 0.4em; border: none; }
</style>

<title>Return type deduction and SFINAE</title>
</head>

<body>

<table class="header"><tbody>
  <tr>
    <th>Document number:&nbsp;&nbsp;</th><th> </th><td>P0238R1</td>
  </tr>
  <tr>
    <th>Date:&nbsp;&nbsp;</th><th> </th><td>2017-05-08</td>
  </tr>
  <tr>
    <th>Project:&nbsp;&nbsp;</th><th> </th><td>Programming Language C++, Evolution Working Group</td>
  </tr>
  <tr>
    <th>Reply-to:&nbsp;&nbsp;</th><th> </th><td><address>Tomasz Kamiński &lt;tomaszkam at gmail dot com&gt;</address></td>
  </tr>
</tbody></table>

<h1><a name="title">Return type deduction and SFINAE</a></h1>

<h2><a name="intro">1. Introduction</a></h2>

<p>Currently, a failure to deduce funcion's return type results in a hard error.
   In this paper we analyze the impact of this behavior on the existing language;
   in particular, how it compromizes the goals of Concepts Lite:
   descriptive error messages and concept-based overloading.</p>

<p>Secondly, we analyse possible ways of addressing the problems, with the recommendation
   of introducing a special "expression-body" syntax for declaring function template bodies.</p>

<p>The proposed syntax, also allows
   generic lambdas to be used with concept-constrained libraries (like Ranges TS), without
   compromising their major features (better error messages, overloading) while preserving
   their terseness:</p>

<table>
  <tr>
    <th>Problematic statement</th>
    <th>Current workaround</th>
    <th>Proposed solution<th>
  </tr>

  <tr>
    <td colspan="3">
<pre>std::vector&lt;std::strings&gt; strs{ /*...*/ };</pre>
    </td>
  </tr>
  <tr>
    <td>
<pre>ranges::find_if(strs, 
  [](auto const&amp; s) { return s == 2; });
</pre>
    </td>
    <td>
<pre>ranges::find_if(strs, 
  [](auto const&amp; s) -&gt; decltype(s == 2) 
  { return s == 2; });             
</pre>
    </td>
    <td>
<pre>ranges::find_if(strs, 
  [](auto const&amp; s) =&gt; s == 2));
</pre>
    </td>
  </tr>
  <tr>
    <td colspan="3">Problem above: return type deduction in the lambda
      prevents compiler from performing concept checks and generating a short and readable
      error message (see <a href="#concepts.lambda.errors">section 4.2.1</a> for details).
    </td>
  </tr>

  <tr>
    <td colspan="3">
<pre>void on_each_month(day_point start_date, int count, Invokable&lt;day_point&gt; f);
void on_each_month(day_point start_date, int count, Invokable&lt;std::string_view&gt; f);</pre>
    </td>
  </tr>
  <tr>
    <td>
<pre>on_each_month(
  day_point{2017_y/10_m/20_d}, 10,
  [](auto d) { return day_of_week(d) == friday; });</pre>
    </td>
    <td>
<pre>on_each_month(
  day_point{2017_y/10_m/20_d}, 10,
  [](auto d) -&gt; decltype(day_of_week(d) == friday)
  { return day_of_week(d) == friday; });</pre>
    </td>
    <td>
<pre>on_each_month(
  day_point{2017_y/10_m/20_d}, 10,
  [](auto d) =&gt; day_of_week(d) == friday);</pre>
    </td>
  </tr>
  <tr>
    <td colspan="3">Problem: return type deduction prevents the compiler
      from performing concept checks and selecting <code>day_point</code> overload,
      which makes the code ill-formed instead (see <a href="#concepts.lambda.overloading">section 4.2.2</a> for details).
    </td>
  </tr>


  <tr>
    <td colspan="3">
<pre>struct Employee
{
   std::string const&amp; first_name() const;
   std::string const&amp; last_name() const;
};
std::vector&lt;Employee&gt; ve{ /*...*/ };</pre>
    </td>
  </tr>
  <tr>
    <td>
<pre>ranges::sort(ve, std::less&lt;&gt;{},
  [] (Employee const&amp; e) { return e.first_name(); });</pre>
    </td>
    <td>
<pre>ranges::sort(ve, std::less&lt;&gt;{},
  [] (Employee const&amp; e) -&gt; decltype(auto) 
  { return e.first_name(); });</pre>
    </td>
    <td>
<pre>ranges::sort(ve, std::less&lt;&gt;{}, 
  [] (Employee const&amp; e) =&gt; e.first_name());</pre>
    </td>
  </tr>
  <tr>
    <td colspan="3">Problem: lambda by default returns by value,
      this creates unnecessary copies of <code>std::string</code> objects
      (two copies for each performed comparison). Use of <code>decltype</code>-based 
      return type eliminates the problem, and will remain correct if function
      is changed to return <code>std::string_view</code> by value
      (see <a href="#expr-body.projections">section 6.1</a> for details).
    </td>
  </tr>

</table>

<!--h2><a name="toc">Table of contents</a></h2-->

<!--h2><a name="revision">Revision history</a></h2-->

<h2><a name="history">2. Revision history</a></h2>

<h3><a name="history.r1">2.1. Revision 1</a></h3>

<p>This revision of the paper extends the motivation section to better illustrate
   the impact on the Concept TS features. In addition, the discussion section
   was extended to include the discussion of alternative solutions for the
   problem.</p>

  <p>Finally, we no longer propose to treat <em>any</em> return type deduction failure as 
   a SFINAE-able error. Instead, we propose to narrow the solution to certain set of cases,
   explicitly designated by the use of expression body syntax: <code>=&gt; expr</code> that was 
   proposed in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0573r0.html">
   P0573R0: Abbreviated Lambdas for Fun and Profit</a>.</p>

<h2><a name="motivation">3. Motivation and Scope</a></h2>

<p>Let's consider the following implementation of <code>variadic_negator</code> &mdash; 
   an adapter that takes a predicate and retuns the opposite value to the one returned by the original:</p>
<pre>
template&lt;typename F&gt;
class variadic_negator_functor
{
  F f_;

public:
  explicit variadic_negator_functor(F a_f) : f_(a_f) {}

  template&lt;typename... Args&gt;
  auto operator()(Args&amp;&amp;... args)
    -&gt; decltype(!this-&gt;f_(std::forward&lt;Args&gt;(args)...))
  { return !this-&gt;f_(std::forward&lt;Args&gt;(args)...); }
};

template&lt;typename F&gt;
variadic_negator_functor&lt;F&gt; variadic_negator(F f) 
{
  return variadic_negator_functor&lt;F&gt;(std::move(f));
}
</pre>

<p>In the implementation, we can see an obvious repetition of the function invocation
   that is placed both in the return type declaration and in the return statement.
   We could think of avoiding this repetition by using function's return type deduction 
   and declare the return type as <code>decltype(auto)</code>. But the resulting code would 
   not have the same semantics as before.
   As a consequence of this change we would affect the overload resolution for calls to
   <code>operator()</code>: when we used <code>decltype(expr)</code> this candidate
   was not considered viable in cases when <code>f</code> was not callable
   with <code>args...</code>, or the result of the invocation could not be negated. 
   In contrast, when we use <code>decltype(auto)</code> this candidate is always viable.</p>
  
<p>When there is only one <code>f</code> to be considered as candidate to call
   <code>variadic_negator(f)(args...)</code>, the nuanced difference explained above might seem
   irrelevant, as it will only change the error messages caused by invalid invocations
   of the functor from "no viable candidate found" to "(long) error instantiatin a template".
   However, the negative results are more grave when the viability/validity of other functions
   depends on the validity of the call to <code>variadic_negator</code>.
   For example, consider the following:</p>
<pre>
template&lt;typename F&gt;
auto invoke(F f) -&gt; decltype(f(0));             //1

template&lt;typename F&gt;
auto invoke(F f) -&gt; decltype(f(std::string())); //2

bool test(int);
</pre>

<p>Given the above declarations the expression <code>invoke(&amp;test)</code> is valid and selects
   the first overload, the same applies to <code>invoke(variadic_negator(&amp;test))</code>
   if <code>decltype(expr)</code> is used for return type.
   However, in case of <code>decltype(auto)</code> return type, the code will emit hard error
   about the lack of conversion from <code>std::string</code> to <code>int</code>
   while instantiating a function template.</p> 

<p>To understand this situation we need to realize that expression in the form 
   <code>decltype(foo(args...))</code> requires compiler to determine return type of the selected
   overload of <code>foo</code> which in case of function template that uses return type
   deduction leads to its instantiation, and according to the current wording, any error occurring 
   in such instantiation is considered to be a hard-error. In case of our example the expression
   <code>invoke(variadic_negator(&amp;test))</code> leads to the instantiation of
   <code>operator()</code> with <code>std::string&amp;&amp;</code>, and
   that is invalid as <code>&amp;test</code> accepts only integers.</p>

<h3><a name="motivation.std_function">3.1. Functions accepting <code>std::function</code> as parameter</a></h3>

<p>In may be pointed out that the presented example of the <code>invoke</code> function is artificial
   and such situations will not normally occur in the code. However, as the result of resolution
   of the <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4485.html#2132">LWG issue #2132</a> 
   a similar mechanism is used to eliminate <code>std::function</code> constructor in case when
   the object is not callable. As a consequence, the same problem would occur in case <code>invoke</code>
   was defined as follows:</p>
<pre>
bool invoke(std::function&lt;bool(int)&gt; f);         //1
bool invoke(std::function&lt;bool(std::string)&gt; f); //2
</pre>

<h3><a name="motivation.lambda">3.2. Interoperation with lambda functions</a></h3>

<p>The problem described by this paper occurs only in situations when a
   functor with templated <code>operator()</code> that uses return type deduction is passed
   to a function. As writing such classes is rarely involved in day-to-day programming, its
   impact may be neglected as affecting only experts, that should be aware of above SFINAE 
   nuances and design their code accordingly.</p>

<p>However, with generic lambdas in C++14 we have introduced short syntax for declaring
   such classes; for example expression <code>[](auto x) { return !test(x); }</code> creates
   an unnamed class with template <code>operator()</code> that uses <code>auto</code>
   return type deduction. As a consequence programmers are exposed to the quirks
   of the detailed behavior of return type deduction even in simple code,
   like lambda implementation of a negator.</p>

<p>If we consider the following declarations:</p>
<pre>
bool test(int);

bool invoke(std::function&lt;bool(int)&gt; f);         //1
bool invoke(std::function&lt;bool(std::string)&gt; f); //2
</pre>
<p>The call to <code>invoke</code> with <code>[](int x) { return !test(x); }</code> is well
   formed and selects the first overload, while seemingly identical expression that uses the new generic lambda 
   <code>[](auto x) { return !test(x); }</code> produces a hard error.</p>

<p>Furthermore, it is worth noting that in the above example the created closure is not used in a polymorphic
   way and we could use an <code>int</code> parameter. However, it may be pointed that <code>auto</code>
   lambda parameters lead to better code, in the same way as in the use <code>auto</code> instead
   of specific type in variable declarations (no unintended conversion, better maintainability, etc...).</p>


<h2><a name="concepts">4. Impact on Concepts</a></h2>

<p>In this section, we discuss the relation between SFINAE (and by consequence proposed feature) and
   currently proposed Concept TS design.</p> 


<h3><a name="concepts.sfinae-usage">4.1. Concepts checks depends on SFINAE</a></h3>

<p>One of major additions of the proposed Concepts design is the <code>requires</code> expression,
   which provides a simple syntax for checking the validity of a given expression performed on
   given types. However, the underlying mechanism is same as in existing expression-SFINAE implementations,
   and in order to work properly it requires that functions used in expression must be properly <i>constrained</i>: 
   they should not be viable candidates unless they body is well formed. As a consequence, it is required that 
   the operations tested in constrained function must also be constrained.</p>

<p>To illustrate the above point, let's consider the following (very simplified) concept hierarchy and constrained
   generic function overloads:</p>
<pre>
template&lt;typename I&gt;
concept bool ForwardIterator = requires (I i) { ++i; };

template&lt;typename I&gt;
concept bool BidirectionalIterator = ForwardIterator&lt;I&gt; &amp;&amp; requires (I i) { --i; };

template&lt;typename I&gt;
  requires ForwardIterator&lt;I&gt;
I rotate(I first, I last) { 
  /* fake body that uses provided operations */ 
  ++first; return first; 
}

template&lt;typename I&gt;
  requires BidirectionalIterator&lt;I&gt;
I rotate(I first, I last) { 
  /* fake body that uses provided operations */ 
  ++first; --first; return first;
}
</pre>
  
<p>Now, consider the following types modelling the above concepts:</p>
<pre>
struct Iter { 
  Iter operator++() { return *this; }; 
}; 

template&lt;typename I&gt;
class FixedReturnWrapper
{
  I pos;

public:
  FixedReturnWrapper(I s) : pos(s) {}

  FixedReturnWrapper operator++() { return FixedReturnWrapper(++pos); }
  FixedReturnWrapper operator--() { return FixedReturnWrapper(--pos); }
};

template&lt;typename I&gt;
class AutoReturnWrapper
{
  I pos;

public:
  AutoReturnWrapper(I s) : pos(s) {}

  auto operator++() { return AutoReturnWrapper(++pos); }
  auto operator--() { return AutoReturnWrapper(--pos); }
};

template&lt;typename I&gt;
class DecltypeReturnWrapper
{
public:
  I pos;

  DecltypeReturnWrapper(I s) : pos(s) {}
};

template&lt;typename I&gt;
auto operator++(DecltypeReturnWrapper&lt;I&gt;&amp; d)
  -&gt; decltype(DecltypeReturnWrapper&lt;I&gt;(++d.pos)) { return DecltypeReturnWrapper&lt;I&gt;(++d.pos); }

template&lt;typename I&gt;
auto operator--(DecltypeReturnWrapper&lt;I&gt;&amp; d) 
  -&gt; decltype(DecltypeReturnWrapper&lt;I&gt;(--d.pos)) { return DecltypeReturnWrapper&lt;I&gt;(--d.pos); }


template&lt;typename I&gt;
class ConceptWrapper
{
  I pos;

public:
  ConceptWrapper(I s) : pos(s) {}

  auto operator++() requires ForwardIterator&lt;I&gt; { return ConceptWrapper(++pos); }
  auto operator--() requires BidirectionalIterator&lt;I&gt; { return ConceptWrapper(--pos); }
};
</pre>

<p>Finally, let's consider the effects of the following calls (<a href="https://wandbox.org/permlink/GTpTFNSZUeSyLfiz">live example</a>):</p>
<pre>
rotate(Iter{}, Iter{});                                               //1
rotate(FixedReturnWrapper{Iter{}}, FixedReturnWrapper{Iter{}});       //2
rotate(AutoReturnWrapper{Iter{}}, AutoReturnWrapper{Iter{}});         //3
rotate(DecltypeReturnWrapper{Iter{}}, DecltypeReturnWrapper{Iter{}}); //4
rotate(ConceptWrapper{Iter{}}, ConceptWrapper{Iter{}});               //5
</pre>

<p>Invocations with types <code>Iter</code> (1), <code>DecltypeReturnWrapper&lt;Iter&gt;</code> (4) and
   <code>ConceptWrapper&lt;Iter&gt;</code> (5) are calling the <code>rotate</code> overload that is
   accepting the <code>ForwardIterators</code>.
   This is a desired and expected behaviour, as operations on these types are properly <i>constrained</i>: candidate functions
   are viable candidates <em>only</em> when they body is well formed. This is achieved by either by being a non-template function
   or by the usage of expression SFINAE (possibly in the form of a <code>requires</code> clause).</p>

<p>Regretfully, in the case of the <code>FixedReturnWrapper&lt;Iter&gt;</code> (2) we encounter a compilation error from body of
   <code>FixedReturnWrapper::operator--</code>, which is called from <code>rotate</code> function. This is caused 
   by the fact that <code>operator--</code> for this type is effectively <i>underconstrained</i>: it is considered to be
   a viable candidate even if its body is ill formed, which in consequence leads to a false-positive result of
   <code>Bidirectional&lt;FixedReturnWrapper&lt;Iter&gt;</code> check
   and the selection of the wrong overload of function <code>rotate</code>.</p>

<p>Finally, in case of the <code>AutoReturnWrapper&lt;Iter&gt;</code> (3) we encounter a compilation error from the body of 
   the <code>FixedReturnWrapper::operator--</code> instantiated while performing return type deduction for the <code>auto</code>
   placeholder. As any other error occurring during function template body instantiation, it is treated as hard error;
   this basically stops the compilation, thus limiting the ability to select the appropriate overload of function <code>rotate</code>
   (the <code>ForwardIterator</code> version).</p>

<p>As we can see from the example of the <code>FixedReturnWrapper&lt;Iter&gt;</code> (2)
   and <code>AutoReturnWrapper&lt;Iter&gt;</code> (3),
   concepts fails to deliver their major promises: more readable error messages and overloading based on
   the subsumption in situation when they are fed with an unconstrained argument.</p>


<h3><a name="concepts.lambda">4.2. Lambdas as arguments for constrained parameters</a></h3>

<p>The introduction of C++14's generic lambdas provides an easy way to create unconstrained functors, which will
   be prone to problems described in the previous section, when they are used with constrained functions. 
   At the same time one of the major usages for the lambda-expression is to provide predicates/functors
   for the STL algorithms, which was selected as flagship conceptualized library.
   In addition, due to the ad-hoc nature of the lambdas, the possibility that the Standard Library will already
   contain matching concepts
   (like in the <code>Iterator</code> case) is very limited; which leaves users with only a few workarounds, like:
   creating their own concepts (which in contrast to lambdas cannot be local to function), or using expression SFINAE or ad-hoc
   requires clauses (which introduces code repetition).</p>


<h4><a name="concepts.lambda.errors">4.2.1. Error messages</a></h4>

<p>To illustrate the problem with error messages, let's consider following simplified version of <code>find_if</code>
   (we focus only on the functor constrain):</p>
<pre>template&lt;typename F, typename... T&gt;
concept bool Predicate = requires(F f, T... t) {
    { f(t...) } -&gt; bool;
};

template&lt;typename Iterator, Predicate&lt;typename std::iterator_traits&lt;Iterator&gt;::reference&gt; F&gt;
Iterator find_if(Iterator b, Iterator e, F f);
</pre>

<p>and the following invocations (<a href="https://wandbox.org/permlink/zvf0b4AENETCoeek">live example</a>):</p>
<pre>
std::vector&lt;std::string&gt; vs;
find_if(vs.begin(), vs.end(), [](auto const&amp; s) { return s == 2; }); //1
find_if(vs.begin(), vs.end(), [](auto const&amp; s) -&gt; decltype(s == 2) { return s == 2; }); //2
</pre>

<p>In the case of the second invocation (2), the compiler will produce a short error message indicating
   that the suplied lambda does not model concept <code>Predicate</code>:</p>
<pre>
prog.cc:11:10: note:   constraints not satisfied
 Iterator find_if(Iterator b, Iterator e, F f);
          ^~~~~~~
prog.cc:6:14: note: within 'template&lt;class F, class ... T&gt; concept const bool Predicate&lt;F, T ...&gt; [with F = main()::&lt;lambda(const auto:1&amp;)&gt;; T = {std::__iterator_traits&lt;__gnu_cxx::__normal_iterator&lt;std::__cxx11::basic_string&lt;char&gt;*, std::vector&lt;std::__cxx11::basic_string&lt;char&gt; &gt; &gt;, void&gt;::reference}]'
 concept bool Predicate = requires(F f, T... t) {
              ^~~~~~~~~
prog.cc:6:14: note:     with 'main()::&lt;lambda(const auto:1&amp;)&gt; f'
prog.cc:6:14: note:     with 'std::__cxx11::basic_string&lt;char&gt;&amp; t#0'
prog.cc:6:14: note: the required expression 'f(t ...)' would be ill-formed
</pre>

<p>However, in case of the first invocation (1), the compiler produces a few hundreds of lines of errors,
   mentoining the lack of comparision operator between <code>std::string</code> and <code>int</code>.
   This is caused by the fact that the concept check for parameter <code>F</code> of <code>find_if</code>
   (performed in both examples) requires the determination of the return type for the supplied functor with
   <code>std::string&amp;</code> argument. In case of the first example, this triggers return type deduction for the lambda,
   and in consequence leads to its instatation with provided arguments &mdash; as the error produced from this instatiation
   is a hard error, compiler emits error from the instation, and stops processing in the middle of the concept check.</p>

<h4><a name="concepts.lambda.overloading">4.2.2. Overloading using concepts</a></h4>

<p>Let's consider the case of the following <code>on_each_month</code> functions:</p>
  
<pre>void on_each_month(day_point start_date, int count, Invokable&lt;day_point&gt; f);
void on_each_month(day_point start_date, int count, Invokable&lt;std::string_view&gt; f);</pre>

<p>The goal of this function is to call the provided functor for the specified <code>start_date</code>, and then
   same day next month, two months in the future, up to <code>count - 1</code> months in future. The first
   overload provides a <code>day_point</code> in the numeric format (we assume very similar format to 
   <a href="https://github.com/HowardHinnant/date">Howard Hinnant's date library</a>). The second is an 
   optimized version that feeds provided function with the serialized date &mdash; by providing this overload
   we can avoid performing multiple allocations and perform operation directly on this representation (incrementing
   month).</p>

<p>Now, consider the following invocation, where <code>day_of_week</code> is a library function
   returning a <code>duration</code> since midnight:</p>
<pre>on_each_month(day_point{2017_y/10_m/20_d}, 10, [](auto d) { return day_of_week(d) == friday; });</pre>

<p>This code will fail to compile, due to an error in the return type deduction for the invocation of the
   lambda with the object of type <code>std::string_view</code>, caused by checking the viability of the second
   overload of <code>on_each_month</code>.
   To fix above problem the user is required to either:</p>
<ul>
  <li>change the lambda parameter type to <code>day_point</code></li>
  <li>constrain the lambda with whatever function <code>day_of_week</code> is constrained with</li>
  <li>infer constrains from <code>day_of_week</code> by specifying the lambda's return type:
      <code>[](auto d) -&gt; decltype(day_of_week(d) == friday) { return day_of_week(d) == friday };</code></li>
</ul>

<p>Each of the above solutions introduces code repetition: either via repeating entire lambda body,
    or reproducing the constrains of the library function; or makes the code less robust by committing to the
    specific resolution of time (using fixed <code>time_point</code> type).</p>


<h2><a name="solutions">5. Alternative solutions</a></h2>

<p>In this section we examine possible solutions for <i>constraining</i> function templates,
   discussing their viability and impact both on the future of the language and on user programs.</p>

<h3><a name="solutions.concepts">5.1. Constraining functions using Concepts</a></h3>

<p>The most obvious solution is to assume (and, in consequence, require) that all operations on objects used with
   the argument, will be constrained via appropriate concepts.</p>

<p>First, let's consider the already presented example of class <code>varidatic_negator</code>, defined as 
   follows:</p>
<pre>
template&lt;typename F&gt;
class variadic_negator_functor
{
  F f_;

public:
  explicit variadic_negator_functor(F a_f) : f_(a_f) {}

  template&lt;typename... Args&gt;
  decltype(auto) operator()(Args&amp;&amp;... args)
  { return !this-&gt;f_(std::forward&lt;Args&gt;(args)...); }
};
</pre>

<p>At first glance we may consider that this function can be easily constrained by the standard concept
   <code>Predicate</code>:</p>
<pre>
template&lt;typename F&gt;
class variadic_negator_functor
{
  F f_;

public:
  explicit variadic_negator_functor(F a_f) : f_(a_f) {}

  template&lt;typename... Args&gt;
    requires Predicate&lt;F&amp;, Args...&gt;
  decltype(auto) operator()(Args&amp;&amp;... args)
  { return !this-&gt;f_(std::forward&lt;Args&gt;(args)...); }
};
</pre>

<p>A closer look into this code exposes a flaw in this approach: by using concept  <code>Predicate</code>
   we impose the restriction on the <code>F</code> return type, to be convertible to <code>bool</code>.
   In contrast, the original design supported any object on which <code>operator!</code> can be applied.
   To restore original functionality we may relax the constraint, by using concept <code>Invokable</code> instead:</p>
<pre>
template&lt;typename F&gt;
class variadic_negator_functor
{
  F f_;

public:
  explicit variadic_negator_functor(F a_f) : f_(a_f) {}

  template&lt;typename... Args&gt;
    requires Invokable&lt;F&amp;, Args...&gt;
  decltype(auto) operator()(Args&amp;&amp;... args)
  { return !this-&gt;f_(std::forward&lt;Args&gt;(args)...); }
};
</pre>
<p>However, in this situation, the function again becomes <i>underconstrained</i>, as we no longer
   guarantee the call to <code>operator!</code> on the result of <code>f</code> will be 
   well-formed.</p>

<p>Finally, to restore the original functionality of this simple class, we need to combine concept
   <code>Invokable&lt;F&amp;, Args...&gt;</code> with an additional constrain, requiring result type
   being negatable. To achieve this we will need to add an ad-hoc expression constraint
   (by using <code>decltype(expr)</code> or an in-place <code>requires</code> expression),
   which will re-introduce the code repetition that we want to eliminate.</p>

<p>Alternatively, we may consider introducing a (set of) standardized syntactic constrains
   (like <code>has_negation</code>) to be used in such situation, but by doing so, we will
   essentially recreate a failed pre-C++11 concept design with its overwhelming number
   of concepts.</p>

<h3><a name="solutions.body-sfinae">5.2. Turning <em>any</em> return type deduction failure into soft-error</a></h3>

<p>In this section, we discuss the originally proposed solution, based on turning the return type deduction failures
   into soft errors. Choosing this solution will basically resolve all the problems related to uses of
   <code>auto</code> return type, by making them <i>constrained</i>: they, by definition, will be viable
   if and only if their body is well-formed.</p>

<p>However, it has its own drawbacks. Firstly it promotes defining the interface of the function
   in terms of terms of its implementation. This should be considered a bad practice for the large
   function, like generic algorithms. For example, we may consider the implementation of the sort algorithm,
   that will be based on the quick-sort, which performs only swap on the elements. 
   Inferring interface (requirement on accepted iterators and their value types) from this implementation, will
   prevent future optimization, like switching to insertion sort after a certain number of recursive calls, 
   as they will require additional operations on the types (creating an copy), and will break existing calls.</p>

<p>Secondly, this approach effectively requires compilers to be able to backtrack from any error
   occurring in the function body. As a consequence, newly introduced language features would
   need to be considered in this context. This will increase the amount of work required to
   introduce any new language features, like constexpr-if or structured binding, that are currently
   limited to function bodies and cannot appear in unevaluated context. In the end such solution would cripple the 
   evolution of the language.</p>

<p>Finally, the complexity of the implementation of such feature, suggest an alternative solution that
   will reduce SFINAE-able errors to only certain constructs in function bodies.
   One of the options, is to limit this capability only to functiosn with a body consisting
   of a single return statement. The author believes that such design will be very surprising for the
   user, because even a simple refactoring, like splitting large expression and saving their result in local 
   variables, will cause previously well-formed code to emit compilation error from a seemingly unrelated
   template instantiation. For reference, please check the difference between invocation of (4) 
   and (2) from <a href="#concepts.sfinae-usage">4.1. Concepts checks depends on SFINAE</a>
   section.</p>

<h2><a name="expr-body">6. Proposed solution: expression body</a></h2>

<p>In this chapter, we present the recommended solution for the problem of constraining small functions,
   based on the introduction of the new function definition syntax, limited to functions consisting
   of a single expression. We propose to reuse the syntax proposed in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0573r0.html">
   P0573R0: Abbreviated Lambdas for Fun and Profit</a>, and making <code>=&gt; expr</code>
   syntax equivalent to <code>noexcept(noexcept(expr)) -&gt; decltype((expr)) { return expr; }</code>,
   while extending it to be allowed for all functions (including member functions).</p> 

<p>To illustrate, the following definition:</p>
<pre>template&lt;typename C&gt; auto size(C const&amp; c) =&gt; c.size();</pre>
<p>will have same semantics as:</p>
<pre>template&lt;typename C&gt; auto size(C const&amp; c) noexcept(noexcept(c.size())) -&gt; decltype((c.size())) { return c.size(); }</pre>

<p>It allows to infer the interface of the function (including SFINAE validity) from its implementation, but 
   still can be combined with a proper concept constraint; for instance, in case of the
   <code>varidatic_negator_example</code>, we can still express requirement of callability 
   via a concept (and benefit from better error messages), without losing the flexibility and correctness of 
   original solution:</p>
<pre>
template&lt;typename F&gt;
class variadic_negator_functor
{
  F f_;

public:
  explicit variadic_negator_functor(F a_f) : f_(a_f) {}

  template&lt;typename... Args&gt;
    requires Invokable&lt;F&amp;, Args...&gt;
  auto operator()(Args&amp;&amp;... args) =&gt; !this-&gt;f_(std::forward&lt;Args&gt;(args)...);
};
</pre>
  
<p>This feature also enables a practice for constraining templates indicated in [6], 
   that apart form decent concepts will well defined axioms and semantics you sometimes need a simpler,
   pure syntactic constraint. The expression body allows such limited syntactic only constraints for ad-hoc use,
  while leaving full intuitive, axiomatized constraints for <code>concept</code>s.
  </p>

<p>Also, limiting the function body to a single expression is actually beneficial,
   as it prevents overuse of the facility for larger functions (like the discussed sort example),
   while still covering large amount of use cases.</p>

<h3><a name="expr-body.projections">6.1. Using lambda to write projections</a></h3>

<p>The return type deduction for a lambda, that returns an object by copy (the <code>auto</code> deduction),
   is not well suited for writing projections (lambda returning a member, calling a getter). 
   This will become more important in the case of the 
   <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4651.pdf">Ranges TS</a>
   that allow projections to be provided to the standard algorithm.</p>

<p>For example, in the following code:</p>
<pre>
struct Employee
{
   std::string first_name;
   std::string last_name;
};
std::vector&lt;Employee&gt; ve{ /*...*/ };</pre>

<p>The invocation of the form:</p>
  
<pre>ranges::sort(ve, std::less&lt;&gt;{}, [] (Employee const&amp; e) { return e.first_name; });</pre>
  
<p>will create two copies of string <b>for each</b> comparison of employee objects,
    creating <code>O(ve.size())</code> unnecessary copies of the string in total.</p>

<p>In contrast, if we declare projection using the proposed expression body syntax:</p>
  
<pre>ranges::sort(ve, std::less&lt;&gt;{}, [] (Employee const&amp; e) =&gt; e.first_name);</pre>
  
<p>the lambda will return a (const) reference to the string (note the use of double parentheses in <code>decltype((expr))</code>),
   and the code will not create any additional copies compared to an STL1-like solution:</p>
  
<pre>ranges::sort(ve, [] (Employee const&amp; e1, Employee const&amp; e2) { return e1.first_name &lt; e2.first_name; });</pre>

<p>Furthermore, in case of predicates (and other lambdas producing a new object), result
   will be returned by value regardless of which syntax is used, so the following lambdas functions 
   will have a <code>bool</code> return type:</p>
<pre>[] (Employee const&amp; e1, Employee const&amp; e2) { return e1.first_name &lt; e2.first_name; }</pre>
<pre>[] (Employee const&amp; e1, Employee const&amp; e2) =&gt; e1.first_name &lt; e2.first_name</pre>


<h3><a name="expr-body.noexcept">6.2. Inferring <code>noexcept</code></a></h3>

<p>We also require an expression body to infer a function exception specification from the 
   returned expression. This matches recent development of the language, that makes 
   <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html">
   exception specification part of type system</a>.</p>

<p>Furthermore, with the direction of terminating the program in case of an exception
   being thrown from an invocation of the functor passed to a parallel algorithm,
   we need the ability to check if a given function may throw an exception, to allow
   a seamless parallelization of user code:</p>
  
<pre>template&lt;typename It, typename F&gt;
void parallel_for(It first, It last, F f)
{
  if constexpr(noexcept(f(*first)))
    std::for_each(std::par, first, last, f);
  else
    std::for_each(first, last, f);
};</pre>
  
<p>We need to avoid the use of <code>std::par</code> execution policy in case
   of a throwing functor, to prevent program from termination, in case of an exception.</p>

<p>Some concerns may be also raised that marking the lambda as <code>noexcept</code>,
   in generic case, requires the insertion of additional handling code that will <code>std::terminate</code>,
   which may negatively impact the performance of a program. 
   However, in the case of the proposed functionality, lambda will be marked as <code>noexcept</code>,
   only  if its body is know not to produce any exceptions (any eventual handling
   code is already present in code of called functions), so adding this specification
   should not impact the generated code.</p> 
   
    
<h2><a name="wording">7. Proposed wording</a></h2>

<p>To be defined.</p>

<p>The general idea is to introduce new expression body syntax, that can be used
   for the normal functions, template functions, and lambda expressions. We propose
   that functions using expression body:</p>
<ul>
  <li>will be implicitly treated as inline,</li>
  <li>need to use trailing return type introducer: <code>auto</code>,</li>
  <li>cannot be forward declared.</li>
</ul>

<p>In case of the potential ambiguity caused by use of lambda with expression body
    as middle argument of the function, we propose to choose shortest possible 
    expression as body, i.e.</p>
<pre>f([](auto x) =&gt; x + 2, 3, 4);</pre>
<p>should be interpreted as:</p>
<pre>f(([](auto x) =&gt; x + 2), 3, 4);</pre>

<h2><a name="implementability">8. Implementability</a></h2>

<p>Proposed expression body is basically a terse notation for existing language syntax, 
   that avoids unnecessary code repetition and can be already simulated using following
   macro:</p>
<pre>#define EXPR_BODY(expr) noexcept(expr) -&gt; decltype((expr)) { return expr; }</pre>

<p>Given above it should not impose major implementation
   problems for compilers already implementing expression-SFINAE.</p>

<h2><a name="acknowledgements">9. Acknowledgements</a></h2>

<p>Andrzej Krzemieński, Casey Carter and Ville Voutilainen offered many useful suggestions and corrections to the proposal.</p>

<h2><a name="literature">10. References</a></h2>

<ol>
  <li>Marshall Clow, 
      "C++ Standard Library Defect Report List (Revision R93)"
      (N4485, <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4485.html">
                       http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4485.html</a>)</li>
  <li>Howard Hinnant, 
      "date library" 
      (<a href="https://github.com/HowardHinnant/date">https://github.com/HowardHinnant/date</a>)</li>

  <li>Barry Revzin, 
      "Abbreviated Lambdas for Fun and Profit" 
      (P0573R0, <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0573r0.html">
                         http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0573r0.html</a>)</li>
  <li>Eric Niebler, Casey Carter,
      "Working Draft, C++ Extensions for Ranges" 
      (N4651, <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4651.pdf">
                       http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4651.pdf</a>)</li>

  <li>Jens Maurer,
      "P0012R1: Make exception specifications be part of the type system, version 5" 
      (P0012R1, <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html">
                         http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html</a>)</li>
  <li>Andrew Sutton, Bjarne Stroustrup,
      "Design of Concept Libraries for C++" 
      (<a href="http://www.stroustrup.com/sle2011-concepts.pdf">http://www.stroustrup.com/sle2011-concepts.pdf</a>)</li>
  
</ol>

</body></html>
