Auto return type deduction surprises

662 views
Skip to first unread message

Marc Mutz

unread,
Sep 26, 2017, 3:55:51 AM9/26/17
to std-pr...@isocpp.org
Hi,

Why do these semantically identical pieces of code have different syntax
rules?

template <typename Map, typename Lookup>
auto find(Map &map, const Lookup &key)
{
const auto it = map.find(key);
if (it == map.end())
return std::nullopt;
else
return std::optional{*it}; // error: inconsistent deduction
for auto return type
}

template <typename Map, typename Lookup>
auto find(Map &map, const Lookup &key)
{
const auto it = map.find(key);
return it == map.end() ? std::nullopt : std::optional{*it}; // OK,
-> std::optional<Map::value_type>
}

iow: Why do auto return types have to have identical types, and don't
just use the rules for the ternary operator?

And why, in the first case, can I not say

auto find(Map &map, const Lookup &key) -> std::optional
{

iow: why doesn't ctor template argument deduction work for trailing
return types?

Is/was this a conscious decision against, or would a proposal
adding/changing these be welcome?

Thanks,
Marc

Richard Smith

unread,
Sep 26, 2017, 4:54:21 AM9/26/17
to std-pr...@isocpp.org
On 26 September 2017 at 00:53, Marc Mutz <marc...@kdab.com> wrote:
Hi,

Why do these semantically identical pieces of code have different syntax rules?

  template <typename Map, typename Lookup>
  auto find(Map &map, const Lookup &key)
  {
      const auto it = map.find(key);
      if (it == map.end())
          return std::nullopt;
      else
          return std::optional{*it}; // error: inconsistent deduction for auto return type
  }

  template <typename Map, typename Lookup>
  auto find(Map &map, const Lookup &key)
  {
      const auto it = map.find(key);
      return it == map.end() ? std::nullopt : std::optional{*it}; // OK, -> std::optional<Map::value_type>
  }

iow: Why do auto return types have to have identical types, and don't just use the rules for the ternary operator?

Because some people thought it more important to allow self-recursive calls after the first return statement:

auto f(int n) {
  if (n <= 1) return 1;
  return n * f(n - 1); // ok to call f here, return type already deduced
}

FWIW, I think we made the wrong tradeoff here, but this decision is unlikely to be reconsidered now.

(We could use the ternary operator rules up until the first recursive use of the function within itself, but that risks major confusion and surprises when a recursive function is refactored and its return statements are reordered...)
 
And why, in the first case, can I not say

   auto find(Map &map, const Lookup &key) -> std::optional
   {

iow: why doesn't ctor template argument deduction work for trailing return types?

An overabundance of conservatism. I expect this restriction will be relaxed as soon as someone argues for it in committee.

Is/was this a conscious decision against, or would a proposal adding/changing these be welcome?

Thanks,
Marc


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/7bf0d2563477e89a8faa3ca2daa18f67%40kdab.com.

Nicol Bolas

unread,
Sep 26, 2017, 11:21:35 AM9/26/17
to ISO C++ Standard - Future Proposals
On Tuesday, September 26, 2017 at 4:54:21 AM UTC-4, Richard Smith wrote:
On 26 September 2017 at 00:53, Marc Mutz <marc...@kdab.com> wrote:
Hi,

Why do these semantically identical pieces of code have different syntax rules?

  template <typename Map, typename Lookup>
  auto find(Map &map, const Lookup &key)
  {
      const auto it = map.find(key);
      if (it == map.end())
          return std::nullopt;
      else
          return std::optional{*it}; // error: inconsistent deduction for auto return type
  }

  template <typename Map, typename Lookup>
  auto find(Map &map, const Lookup &key)
  {
      const auto it = map.find(key);
      return it == map.end() ? std::nullopt : std::optional{*it}; // OK, -> std::optional<Map::value_type>
  }

iow: Why do auto return types have to have identical types, and don't just use the rules for the ternary operator?

Because some people thought it more important to allow self-recursive calls after the first return statement:

auto f(int n) {
  if (n <= 1) return 1;
  return n * f(n - 1); // ok to call f here, return type already deduced
}

FWIW, I think we made the wrong tradeoff here, but this decision is unlikely to be reconsidered now.

Personally, I think it's a good move. Not for self-recursion reasons but for sanity and error checking reasons.

With the ?: operator, the two expressions are right next to each other. This means that if you can see one expression, you can see the other. As such, you can easily see from that one line what the overall expression resolves to. It requires more mental thought, but all of the information is right there in the same statement.

Return statements can be pages apart, depending on function length. As such, it's much more difficult to know what `std::common_type` will resolve to; figuring that out requires bouncing between all of the return statements in the function. So in order to know what the function's return type is, you have to read the entire thing.

Also, since the return statements are potentially far apart, inconsistency between their expressions is more likely to be the result of user error than a deliberate desire to do `std::common_type` shenanigans.

(We could use the ternary operator rules up until the first recursive use of the function within itself, but that risks major confusion and surprises when a recursive function is refactored and its return statements are reordered...)
 
And why, in the first case, can I not say

   auto find(Map &map, const Lookup &key) -> std::optional
   {

iow: why doesn't ctor template argument deduction work for trailing return types?

An overabundance of conservatism. I expect this restriction will be relaxed as soon as someone argues for it in committee.

It also wouldn't work in this case anyway, since `std::nullopt` can't deduce a particular `T` for `optional<T>`. The only way that class template argument deduction would make sense in return values is if all of the return expressions could deduce the arguments and if they all deduce the same type.


T. C.

unread,
Sep 26, 2017, 3:04:01 PM9/26/17
to ISO C++ Standard - Future Proposals
Also, we won't be able to use common_type's semantics, which is order dependent for >2 arguments. That would be insane for this case.

We'd have to generalize ?:'s semantics directly for >2 operands if we want to go down that path.

Faisal Vali

unread,
Sep 26, 2017, 4:25:02 PM9/26/17
to <std-proposals@isocpp.org>
Years ago, when i was interested in this, I had come up with something
similar - my unsophisticated implementation (if i remember correctly)
was O(N^2) in the number of return statements (but i think i felt this
could be optimized...).

Implementation:
https://github.com/faisalv/clang/commits/cxx1z-conditional-return-type-deduction

Test examples: https://github.com/faisalv/clang/blob/cxx1z-conditional-return-type-deduction/test/SemaCXX/cxx1z-deduced-return-type.cpp

I also had some thoughts on how to merge return statements, returning
different lambda expressions/or closure objects...

But I think I agree with Richard, unless some very strong forces raise
some very loud resonant noises, this ship might have sailed for
good...

> --
> You received this message because you are subscribed to the Google Groups
> "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to std-proposal...@isocpp.org.
> To post to this group, send email to std-pr...@isocpp.org.
> To view this discussion on the web visit
> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/072abf8e-251c-4fe5-a24f-80c9319e3326%40isocpp.org.
Reply all
Reply to author
Forward
0 new messages