C++ Logo


Advanced search

Re: Initialisers in ternary operators

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Sat, 12 Sep 2020 23:49:58 -0400
On Sat, Sep 12, 2020 at 11:28 PM Jorg Brown via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Fri, Sep 11, 2020 at 6:29 AM Richard Hodges via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>> On Fri, 11 Sep 2020 at 12:39, Garrett May via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>> Hm .. who said,(in the context of C++ talk): "don't be clever unless you
>>>> really have to"?
>>> I'm not really sure what that means?
>>> Either way, my personal preference would be in support of Richard
>>> Hodges's suggestion. I've found GCC's expression statements to be useful in
>>> the past, and I think standardising it would be a perfect solution for
>>> this. It avoids the weirdness of having an initializer only for ternary
>>> operators that Ville Voutilainen mentioned (due to the syntax of ternaries)
>>> whilst also providing a way for scope to yield a value.
>> All that remains now is for someone to write a paper, in which is laid
>> out the motivating use case, a demonstration that existing
>> language features are limiting productivity and an impact assessment.
>> There is already a compiler supporting the feature, so that's in the
>> paper's favour.
>> I suspect the biggest problems would be:
>> a) convincing everyone that the existing lambda syntax is not sufficient
> Regarding statement expressions, I dislike deviations from the standard in
> Google's code, so I tried to get rid of statement expressions when adopted
> c++11 and got lambdas. And indeed, lambdas solve much of the need.
> But there was one strong outlier, and it looks like this:
> std::optional<std::string> oldFindUsersCity(bool non_default) {
> std::optional<UserId> uid = UserId{};
> if (non_default) {
> uid = GetUserId();
> if (!uid) return nullopt;
> }
> std::optional<Location> uloc = uid->GetLocation();
> if (!uloc) return nullopt;
> return uloc->GetCityName();
> }
> Those if/return pairs make the code really ugly really fast, so people
> made macros:
> std::optional<string> FindUsersCity(bool non_default) {
> UserId uid;
> if (non_default) ASSIGN_OR_RETURN(uid, GetUserId());
> ASSIGN_OR_RETURN(Location uloc, uid.GetLocation());
> return uloc.GetCityName();
> }
> And therein lies the problem. These macros aren't that hard to implement
> with statement expressions (https://godbolt.org/z/G7Wsj3)

Or even just with plain old macros, right? as long as you give up the idea
of making a single macro that can *both* declare a new local variable *and*
have non-local control flow.
https://godbolt.org/z/esz6G1 (notice the sneaky declaration of `Location
uloc;` instead of doing it inside the macro)

If you were going to use this as a motivating example in a paper, you'd
have to do something to "fix it up" so that the simple solution didn't work.
But if your example gets too contrived, people won't buy it, either.


> b) convincing other compiler vendors to bother to implement the feature.
> Already done. gcc and clang and icc and Zapcc already implement it. MSVC
> was the only one I tested, that didn't support it. This is because there's
> a Unix header file that uses the feature.

However, vendors may have radically different ideas about what it means to
e.g. `goto` into a statement-expression, or `goto` out of one, or nest
statement-expressions inside each other. My impression has always been
that vendors treat statement-expressions as a novelty for "compatibility
with someone else," never as a flagship feature in their own right. The
goal is just to keep them from segfaulting and paper over any bugs as
they're filed.

For one thing, there's the issue of destructors. C++03 tightened the rules
> on when destructors were run, which was a big help. But what happens when
> a temporary is used inside of a statement expression? Is it destructed at
> the end of whatever statement (inside the statement expression) used it?
> Is there an exception for the last statement in the expression?

Not to mention, the syntax for statement-expressions is like
({ foo(); })
which looks a heck of a lot like you're discarding the result of foo().
Should it warn if foo() is [[nodiscard]]? Why or why not? (Rhetorical
question to be discussed in a paper, if at all.)

Anyway, "structured programming for the win." If you can write your
algorithm completely with local control structures (if/while/for/function
calls) and abstain from non-local control flow entirely, you'll be a lot
happier — and you won't need statement-expressions.

my $.02,

Received on 2020-09-12 22:50:15