You wrote that this one line:

    if (DECL : EXPR) X else Y;

is equivalent to these 9 lines:

    {
        auto&& __c = EXPR;
        if (__c) {
            DECL = *__c;
            X;
        } else {
            Y;  // noteworthy: DECL is not in scope here
        }
    }

But with the advent of C++11, this is already much much simpler, since the __c assignment can be folded into the if.

Also, it's a bit misleading to combine everything into one line, for comparison purposes.  And the capitalization makes me think of macros.

I would write it this way.  This one line:

  if (type_goes_here decl : expr) X(decl) else Y();

is equivalent to this line:

  if (type_goes_here decl = expr) X(*decl) else Y();

with the exception that decl is not in scope for Y() clause, when this new form of if is used.

I found your std::get_if use case compelling... but for fun I searched Google's internal C++ for uses and found 4 already.  One use was:

    const PacketVariant packet = ...expr...;
    if (auto packet_ptr = std::get_if<PacketPtr>(&packet)) {
      ScheduleSendPacket(*packet_ptr);
    } else if (auto packet_ptr = std::get_if<InjectablePacketPtr>(&packet)) {
      InjectPacket(*packet_ptr);
    }

As you can see, the fact that the first packet_ptr was still in scope, didn't prevent us from defining a second packet_ptr with a different type.  And I think that allowing them both to have the same name would have been the primary benefit of the second packet_ptr not being in scope.

So, sorry, I don't find the extra implicit * worthy of a new language syntax.

-- Jorg



On Sat, Oct 26, 2019 at 9:15 AM Andrew Tomazos via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
We propose a new variant of the if statement that fuses a condition declaration with an indirection in the true branch.  We call it an 'indirect if statement'.

An indirect if statement of the form:

    if (DECL : EXPR) X else Y;

is equivalent to:

    {
        auto&& __c = EXPR;
        if (__c) {
            DECL = *__c;
            X;
        } else {
            Y;  // noteworthy: DECL is not in scope here
        }
    }

So for example:

int* get();

int main() {
    if (int x : get())
        return x;
    else
        return EXIT_FAILURE;
}

void f(std::variant<A,B,C> V) {
    if (auto x : std::get_if<A>(V)) {
       /*...*/
    } else if (auto x : std::get_if<B>(V)) {
       /*...*/
    } else if (auto x : std::get_if<C>(V)) {
       /*...*/
    }
}

template<typename D, typename B>
D* derived_if(B& b) {
      return dynamic_cast<D*>(&b);  
}

void g(Animal& a) {
    if (auto& dog : derived_if<Dog>(a)) {
        /*  ... */
    } else if (auto& cat : derived_if<Cat>(a)) {
        /*  ... */
    } else if (auto& pig : derived_if<Pig>(a)) {
        /*  ... */
    }
}

T* ptr;
std::optional<T> opt;
std::shared_ptr<T> sptr;
std::unique_ptr<T> uptr;

int main() {
    if (T x : ptr)
        /*...*/;

    if (T x : opt)
        /*...*/;

    if (T x : sptr)
        /*...*/;

    if (T x : uptr)
        /*...*/
}

The motivation should be self-evident enough from the examples for this first note.

Initial thoughts appreciated.
   -Andrew.

--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals