C++ Logo

std-proposals

Advanced search

Re: Allow block statement before each member in intializer list

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Tue, 23 Jul 2019 21:44:45 -0400
On Tue, Jul 23, 2019 at 8:31 PM Joshua Cannon via Std-Proposals <
std-proposals_at_[hidden]> wrote:

>
> As a (contrived) example, let's say I have a class (called Foo) that
> takes in a const-reference to a vector. It is going to store a reversed
> copy of the vector with all of the elements also incremented as a member.
> Additionally, it is going to have a member of type Bar, which is
> constructed by taking in iterators to the reversed-and-incremented vector.
> There's no easy way to do that today. Some solutions might be [...]
>

A proposal with this degree of "real-estate claimage" needs to have better
motivation than a single contrived example where you already know how to
work around it anyway.

I say that your proposal claims a lot of syntactic (and mental) real-estate
because it appears to collide with the long-desired feature of
"member-initializer-list with trailing comma." Consider:

struct S1 {
    int x, y, z;
    S1() : x(1), y(2), z(3),
    {}
};

Under your proposal, IIUC, this would be ill-formed code: the compiler
would see `};` when it was expecting another `,` followed by a
member-initializer. But there's already a fair bit of support (from the
machine-generated-code crowd) for this to be well-formed and for the
compiler to simply ignore the trailing `,` after z's member-initializer.

You could certainly deal with this by tweaking your proposed syntax — like
maybe interpose some sort of keyword, or use `( ... )` instead of `{ ... }`
to delimit your code blocks, or whatever. But that brings me back to the
proposal's very weak motivation (about which, more below).

You also mentioned another syntactic issue that I think is a bigger deal
than you're making it. Consider

struct S2 {
    int x, y, z;
    S2(int) : x(1), { ++x; }, y(2), z(3) {}
    S2(short) : x(1), { ++(this->x); }, y(2), z(3) {}
    S2(char) : x(1), { S2 *p = this; ++(p->x); }, y(2), z(3) {}
};

Is `S2(int)` well-formed? You'd say "yes," obviously.
Is `S2(short)` well-formed? It's just syntactic desugaring of `S2(int)`,
right?
Is `S2(char)` well-formed? If so, then how do you propose to stop people
from accessing `p->y`?

Lastly, you haven't mentioned what you'll do about

struct S3 {
    int x, y, z;
    S3(int) : z(3), { puts("hello"); }, y(2), { puts("world"); }, x(1) {}
};

nor

struct S4 {
    int x, y, z;
    S4(int) : x(1), { return; }, y(2), z(3) { puts("so this is never
reached?"); }
};


Okay, now I want to return to the weak motivation, by showing how I'd
refactor your example code to work in today's C++.

Example:
>
> class Foo {
> std::vector<int> myVec;
> Bar bar;
>
> public:
> Foo(const std::vector<int>& inVec):
> myVec(inVec.rbegin(), inVec.rend()),
> {
> std::transform
> (
> myVec.begin(),
> myVec.end(),
> myVec.begin(),
> &[](int i){return ++i;}
> );
> },
> bar(myVec.begin(), myVec.end())
> {}
> };
>

I would write this as

class Foo {
    std::vector<int> myVec;
    Bar bar;
public:
    explicit Foo(const std::vector<int>& inVec) :
        myVec(rev_and_inc(inVec)),
        bar(myVec.begin(), myVec.end())
    {}
private:
    static std::vector<int> rev_and_inc(const std::vector<int>& inVec) {
        std::vector<int> result(inVec.rbegin(), inVec.rend());
        for (int& elt : result) ++elt;
        return result;
    }
};

This code is exactly as efficient as the code you wrote, thanks to copy
elision. It's also easier to read. It's also portable to C++11 compilers.
(In fact, the only reason it's not valid *C++03* is because I turned your
`std::transform` into a C++11 for-loop for readability's sake.)

I hesitate to claim that you'll never find *any* sufficient motivation for
your proposal, but the code you've presented certainly isn't sufficient
motivation to justify a language change (let alone a language change that
claims so much useful real-estate).

HTH,
–Arthur

Received on 2019-07-23 20:46:55