C++ Logo

sg20

Advanced search

Re: [SG20] A draft paper to fix the range-based for loop to make it teachable

From: Nicolai Josuttis <nico_at_[hidden]>
Date: Fri, 13 Nov 2020 13:22:20 +0100
Once again:
The range-based for loop is the ONLY place where the usual rules
for dealing with temporaries and references do NOT apply:

Usually you need:
a) a reference
b) a semicolon

for example:
> auto&& t = get<0>(getTuple());
>
> auto&& v = getTmp().getVals();
>
> auto&& vc = getOptionalInts().value();
>
> auto&& sp = std::span(getVec().data(), 5);


The range-based for loop is the ONLY place where we neither necessarily
see references and semicolon:

> for (auto elem : get<0>(getTuple());
>
> for (auto elem : getTmp().getVals();
>
> for (auto elem : getOptionalInts().value();
>
> for (auto elem : std::span(getVec().data(), 5))

At the same time, this is THE control structure for beginners.
They might know about reference, but no details such as
universal references and subtle details of lifetime extension.

That is the reason we have to fix it only here.

References to temporaries and lifetime extensions is
still hard to teach but I can skip it until it's time to do so.
THE basic loop of C++ I can't skip (Yes, I could try, but then teach
how to use algorithms to copy the square of all elements that are
greater than 100 to another container...).

Hope this helps
  Nico


Am 13.11.2020 um 13:09 schrieb Amir Kirsh via SG20:
> Which brings us back to the idea that the caller is responsible for
> handling temporaries correctly (I think it was Yehezkel Bernat who
> raised it above with the example of std::min getting and returning const
> reference).
>
> In those cases where you see that callers /constantly do not handle
> temporaries correctly /(the getOrDefault example by Louis Brandy
> <https://www.youtube.com/watch?v=lkgszkPnV8g&t=13m0s>) - then go and
> amend the function to handle temporaries differently, or just delete it
> for temporaries.
>
> Bottom line: handling temporaries correctly is something that is
> unavoidable, whether you teach it or just count on the students to get
> to it by themselves at some point.
>
> Is there something special - teaching-wise - for temporaries in
> range-based-for? maybe.
> Can it be solved specifically for temporaries in range-based-for? maybe.
> But I'd argue it wouldn't make it easier to teach. Temporaries are still
> there with their weird behavior.
> Not saying that the proposed fix is bad - it can be useful in
> eliminating potential bugs in range-based-for. But similar bugs in
> other, quite similar code examples, can still occur.
>
>
> On Fri, Nov 13, 2020 at 1:25 PM Peter Sommerlad (C++) via SG20
> <sg20_at_[hidden] <mailto:sg20_at_[hidden]>> wrote:
>
> FWIW
>
> I would not delete the &&-qualified overload but return the string by
> value to avoid leaking a dangling reference in such a case
>
> std::string getName() && { return std::move(name); }
>
> I think that would make your code work again. However, I am not sure,
> this approach is generalizable for the standard library.
>
> In addition we would need to provide rref-qualified overloads of
> functions taking parameters by const-lref that return a reference to
> the
> parameter (or its guts) as well.
>
> std::string const & whatsYourName(Person const &p){ return
> p.getName(); }
> std::string whatsYourName(Person &&p) { return p.getName(); } //
> rely on RVO
>
> Regards
> Peter.
>
> Giuseppe D'Angelo via SG20 wrote on 13.11.20 11:20:
> > On 13/11/2020 10:04, Amir Kirsh via SG20 wrote:
> >>
> >> Taking this approach in users' code is possible, though a bit
> >> cumbersome today:
> >>
> >> class Person {
> >> std::string name;
> >> public:
> >> Person(std::string name): name(std::move(name)) {}
> >> const std::string& getName() const & { return name; }
> >> const std::string& getName() const && = delete;
> >
> > This makes legitimate code not work:
> >
> > std::string name = // not a reference
> > getPerson().getName();
> >
> >
> >> // or:
> >> // std::string getName() const && { return name; }
> >
> > This works but makes a copy instead of moving the data member into
> the
> > return object. You'd also need something like:
> >
> > std::string getName() && { return std::move(name); }
> >
> >
> > Usual disclaimer: you justify this because you know that
> std::string is
> > reasonably cheap to move, so you can afford the cost of return by
> value
> > in these circumstances. If you were to return something not
> necessarily
> > cheap to move, then justifying the return by value -- even from
> rvalue
> > overload -- is not so easy. So you'd return by rvalue reference
> > (otherwise, a chain like getArrayOfPersons().getFirst().getName()
> > creates N possibly expensive temporaries). And we're back to
> square one
> > for the general case.
> >
> > Other usual disclaimer, this assumes that "moved from" means
> "partially
> > formed", not "valid but unspecified" like the stdlib does (you've
> likely
> > just broken class invariants).
> >
> > Moreover: going this route for any "getter" makes the implementation
> > alone, today, terribly cumbersome (this is probably where "deducing
> > this" could help). I still find that the teachability of all of
> this is
> > nothing but dreadful.
> >
> > My 2 c,
> >
> >
> >
>
>
> --
> Peter Sommerlad
>
> Better Software: Consulting, Training, Reviews
> Modern, Safe & Agile C++
>
> peter.cpp_at_[hidden] <mailto:peter.cpp_at_[hidden]>
> +41 79 432 23 32
> --
> SG20 mailing list
> SG20_at_[hidden] <mailto:SG20_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/sg20
> <https://lists.isocpp.org/mailman/listinfo.cgi/sg20>
>
>

-- 
Nicolai M. Josuttis
www.josuttis.de
+49 (0)531 / 129 88 86
+49 (0)700 / JOSUTTIS
Books:
 C++: http://cppstdlib.com, http://cppstd17.com,
      http://tmplbook.com, http://cppmove.com
 SOA: http://soa-in-practice.com

Received on 2020-11-13 06:22:24