On Dec 19, 2024, at 9:07 AM, Richard Hodges via Std-Proposals <std-proposals@lists.isocpp.org> wrote:


Please, show me goto supporting the same semantics of labeled continue:

label1: while (cond) {
   for  (….) {
     if (x) continue label1;
   }
}

extern bool cond();
extern void something(int);

void test()
{
    while (cond()) 
    {
        for  (int i = 0 ; i < 100 ; ++i) {
            if (i == 50) goto continueat1;
            something(i);
        }
    continueat1: ;
    }
}



That breaks if there are any objects with constructors or destructors declared after your goto. A *correct* solution based on goto, instead of the using a *not remotely new* language feature requires:

void f() {
while (cond()) { {
/* loop2: */ for (int i = 0 ; i < 100 ; ++i) { {
if (i == 50)
goto loop1_continue;
if (i == 20)
goto loop1_break;
if (i == 30)
goto loop2_continue;
if (i == 10)
goto loop2_break;
something(i);
} loop2_continue:; } loop2_break: ;
std::shared_ptr<int> example = std::make_shared<int>(1);
} loop1_continue: ; } loop1_break: ;
}

Let’s compare to the labeled continue, as available in (I guess?) c23, or: swift, rust, javascript, java, c#, zig (though the zig label reference syntax is different - `:label:` vs `label` I think):
void f() {
loop1: while (cond()) {
loop2: for (int i = 0 ; i < 100 ; ++i) {
if (i == 50)
continue loop1;
if (i == 20)
break loop1;
if (i == 30)
continue loop2;
if (i == 10)
break loop2;
something(i);
}
std::shared_ptr<int> example = std::make_shared<int>(1);
}
}

Note that it no longer relies on a naming scheme to ensure correct use, it doesn’t need manually inserted additional scopes to “make it work” or be correct. Because it’s not a hack, there’s no risk of any one accidentally making putting code before or after one of the multiple distinct labels (with compiler invisible semantics). Even better, because it does not have a whole bunch of unnecessary make-work, there’s no overhead to just labeling all your loops to be both clearer, and safe against future modifications.

—Oliver