18

I've thought of following code by trying to solve a difficult 'nested-condition' problem:

goto error;
    
if (false)
{
error:
    cout << "error block" << endl;
}
else
{
    cout << "else block" << endl;
}

When I run this code, only error block is displayed, as expected (I guess?). But is this defined behavior across all compilers?

cigien
  • 57,834
  • 11
  • 73
  • 112
Be Ku
  • 319
  • 1
  • 6
  • 4
    We can help to solve your *difficult 'nested-condition' problem*. which should be better than using `goto`. – Jarod42 Jul 05 '21 at 14:17
  • 12
    This is a reasonable language-lawyer question. Lets not let the appearance of a `goto` lead to automatically downvoting the question. Just because you disagree with the premise of the question doesn't make it it a bad question. – François Andrieux Jul 05 '21 at 14:27
  • 6
    It sounds like you are concerned about jumping into unreachable code. You may be worried that the branch will be optimized out. and not exist at runtime. But the fact that the branch has a label and that there is a `goto` to that label means it is not, in fact, unreachable. – François Andrieux Jul 05 '21 at 14:29
  • Although `goto` has a bad reputation, there are a few use cases where it is a good choice (or at least lesser of evils). Objective-C (and by extension, Objective-C++) in Cocoa code often use `goto` to for error detection and jumping to the resource cleanup — albeit, these are cousin languages and not C++ itself. A more strictly C++ scenario would be there are some state machines that can be expressed eloquently with a `goto`, or in a much more contorted/convoluted/complicated way to avoid a `goto` (and likely less efficient... but I ought not underestimate the clever optimizer). – Eljay Jul 05 '21 at 15:28
  • What *usually* happens is the commit gets shot down in code review. Your experience may vary. – n. m. could be an AI Jul 20 '21 at 19:04

2 Answers2

14

Yes, this is well defined. From stmt.goto#1

The goto statement unconditionally transfers control to the statement labeled by the identifier. The identifier shall be a label located in the current function.

There are some restrictions, e.g. a case label cannot cross a non-trivial initialization

goto error;
int i = 42;
error:       // error: crosses initialization of i

But these don't apply to your example. Also, in the case of crossing an initialization, this is a hard compiler error, so you don't have to worry about undefined behavior.


Note that once you jump to the case label error, you're effectively inside the true branch of the if condition, and it doesn't matter that you got there via a goto. So you're guaranteed that the else branch will not be executed.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • I think they may be more concerned that not executing the `if` condition could prevent it from skipping over the `else` block after jumping into the `if`. – Barmar Jul 05 '21 at 14:31
  • @Barmar Yeah, that's true. I've added an explanation about that. – cigien Jul 05 '21 at 14:35
  • Isn't the initialisation of an `int` with an `int` trivial? `std::is_trivially_constructible::value` is `true` – NotNik. Jul 06 '21 at 18:43
  • @NotNik. To avoid terminology issues, the standard uses the term [*vacuous initialization*](https://timsong-cpp.github.io/cppwp/n4861/basic.life#1) (basically meaning "that doesn't do any work") in this case. – bogdan Jul 20 '21 at 21:28
1

My 5 cents:

If your compiler have optimizer, the code is reduced in following way:

// more code here
goto error; // this go directly to the label
    
if (false)
{
error:
    cout << "error block" << endl;
    // this skips else clause
}
else
{
    cout << "else block" << endl;
}
// more code here

So the compiled code become just this:

// more code here
{
    cout << "error block" << endl;
}
// more code here

Here is link to Godbolt:

https://gcc.godbolt.org/z/nY6E166Pz

(I did simplify the code a little bit so assembly is easier to read)

Nick
  • 9,962
  • 4
  • 42
  • 80