8

Why does the following compile in GCC 4.8 (g++)? Isn't it completely ill-formed?

void test(int x)
{
    return test(3);
}

int main() {}
  1. I'm trying to use the result of calling test, which does not exist
  2. I'm trying to return a value from test

Both should be fundamentally impossible — not just UB, as far as I can recall — with a void return type.

The only warning I get is about x being unused, not even anything about a non-standard implicit return type being added.

Live demo

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • It's not about objects being of type `void`. *Expressions* of type `void` are *discarded-value expressions*. – Kerrek SB Jan 04 '14 at 16:59
  • once you call the function however it should result in a `Segmentation fault` – deW1 Jan 04 '14 at 17:00
  • @dan: Or a stack overflow, or some other practical consequence of a non-terminating program with infinite call depth. That could include my program spontaneously developing sentience and telephoning you at work. :) – Lightness Races in Orbit Jan 04 '14 at 17:01
  • 3
    Note: That's not specific to C++11. Since C++98/03 have we been able to write `return f();` instead of `{ f(); return; }` (given `void f();`). TC++PL3 §7.3 (page 148) said “This form of return is important when writing template functions where the return type is a template parameter”. – gx_ Jan 04 '14 at 17:05
  • `void` has been an incomplete type since C++98. Can you please provide information about `void` not being incomplete in C++11 anymore? – Johannes Schaub - litb Jan 04 '14 at 21:08
  • @gx_ dammit, why didn't you put that as an answer :) It wouldn't make me look as someone hunting for rep and copying comments! – Johannes Schaub - litb Jan 04 '14 at 21:16
  • @JohannesSchaub-litb: I'm wrong. `[C++03: 3.9/5]` and `[C++11: 3.9/6]` both state that `void` is incomplete. There is some rule that changes how `void` is handled in C++11, but I can't remember what it is. :( – Lightness Races in Orbit Jan 05 '14 at 07:30
  • @LightnessRacesinOrbit all that I can remember about `void` is that you can do `void f(TypedefForVoid);` now (and I also forgot whether this change was for C++11 or whether it will be for C++14). – Johannes Schaub - litb Jan 05 '14 at 17:53
  • @JohannesSchaub-litb: Apparently I was thinking of `void` being made a literal type in C++14, as discussed in [this answer](http://stackoverflow.com/a/20492630/560648) that I nonetheless deleted because it wasn't quite right for the stated question. – Lightness Races in Orbit Jan 05 '14 at 18:58

2 Answers2

11

That's allowed by the standard (§6.6.3/3)

A return statement with an expression of type void can be used only in functions with a return type of cv void; the expression is evaluated just before the function returns to its caller.

Mat
  • 202,337
  • 40
  • 393
  • 406
  • Yeah, this is correct. The entire thing becomes a no-op. Worth noting that this was the same in C++03, and that it's bloody stupid. – Lightness Races in Orbit Jan 04 '14 at 16:59
  • 2
    @LightnessRacesinOrbit: I'm guessing it's useful in templates where the return type might or might not be `void`. – Mat Jan 04 '14 at 17:00
  • @Mat: I think I'd still have hoped for ill-formity there, and let SFINAE open up other options. If nothing else though it just makes this _really_ horrid for non-template code. :( – Lightness Races in Orbit Jan 04 '14 at 17:00
  • 1
    @LightnessRacesinOrbit: I'm guessing you can't switch to C, which forbids this? (-: – Mat Jan 04 '14 at 17:08
  • 2
    It could have its uses. Given a function `void error_reporter(...)`, you can write code in a function that returns `void` just: `if (something > sommat_else) return error_reporter("the impossible occurred");` instead of needing braces and multiple statements inside them. That could be quite useful. – Jonathan Leffler Jan 04 '14 at 17:08
  • @JonathanLeffler: Mmm I suppose but I still think it's more surprising than it is useful. – Lightness Races in Orbit Jan 04 '14 at 17:33
6

As to why GCC allows it - sure because the Standard requires it to be valid. Building the transitive closure to the rationale of the rule in the Standard, I'm pretty sure that GCC allows this because it's useful in the event of templates

template<typename F>
typename std::result_of<F()>::type call(F f) {
  return f();
}

int main() {
   std::cout << call([]{ return 42; }) << std::endl;
   call([]{ std::cout << "I'm just an outputtor!" << std::endl; });
}

As you see, call did not need to do a special case for void in the return statement. Sort of similar to how x.~T() is allowed even if T ends up as int.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212