23

Having just received a warning from the compiler for this function:

template<class T>
Matrix3x3<T> & operator - (Matrix3x3<T> const & p)
{
    auto m = Matrix3x3<T>(p);

    m.m11 = -m.m11; m.m12 = -m.m12; m.m13 = -m.m13;
    m.m21 = -m.m21; m.m22 = -m.m22; m.m23 = -m.m23;
    m.m31 = -m.m31; m.m32 = -m.m32; m.m33 = -m.m33;

    return m;
}

, I am wondering why returning an address of local variable or temporary doesn't merit an error. Are there circumstances where you have to do it? What's the rationale for this only being "undefined behaviour" and not a language constraint?

I can't think of any.

Robinson
  • 9,666
  • 16
  • 71
  • 115
  • It could be declared static in the function. – OldProgrammer Jun 03 '15 at 11:29
  • 3
    Wouldn't the compiler know it was static though? – Robinson Jun 03 '15 at 11:29
  • 6
    Random number generation ;) – tsuki Jun 03 '15 at 11:30
  • A helping hand to the compiler writers. – Mohit Jain Jun 03 '15 at 11:43
  • It is undefined behavior, which means the behavior is unpredictable, the compiler is not obligated to issue a diagnostic but most will with the proper warning flags. – Shafik Yaghmour Jun 03 '15 at 11:48
  • 1
    If I had to guess, it'd be that some old C code - perhaps implementation side rather than in application libs/code - deliberately returned such values as a way of reporting stack usage to e.g. compare to known stack limits to handle recursion before failure, pick buffer sizes etc.. With inline assembly getting the stack pointer is simpler, but not all compiler had/have that. If doing such things within expressions (perhaps macro substitutions), creating a local var in the calling context wouldn't be an option (sans something like GCC's statement-expressions extension). – Tony Delroy Jun 03 '15 at 11:52
  • 1
    Finding all the places when you use it that would definitely cause undefined behaviour is impossible in the general case (cf. the Entscheidungsproblem). I don't think the Committee will ever mandate that errors be found "when it's possible, but sometimes you can't, so...". (Note that if you never use the return value, there is no problem.) – molbdnilo Jun 03 '15 at 12:02

2 Answers2

19

There is no good reason why it shouldn't be an error, just the C++ standard does not treat this case as such and conforming compilers adhere to the standard.

However, emitting a warning is encouraged:

§12.2.5.2 The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.

[...]

[Note: This may introduce a dangling reference, and implementations are encouraged to issue a warning in such a case. — end note ]

Emphasis is mine.

Community
  • 1
  • 1
Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80
  • Based on the formatting of the C++14 draft standard I linked (see page 271), I cannot tell if the note is referred to **12.2.5.3** only or to the entire **12.2.5**. The latter makes sense, but I'd appreciate if someone else could comment on this. – Stefano Sanfilippo Jun 03 '15 at 11:49
  • N4296 is a draft of C++1z, after C++14 final (which was N4141, with N4140 the last C++14 draft). Regarding your question, as far as I can tell, the example and note refer to [12.2p5.3] only. When examples or notes refer to the whole list, they're indented on the same level as the main paragraph; for an example of such formatting, see [8.5.4p7] (contrasted with the list and examples in [8.5.4p3]). On a more constructive note, your answer only covers temporaries created in the return statement, and not local variables, which are mentioned in the question. – bogdan Jun 03 '15 at 17:08
7

Reason: Lack of consistency in generating compiler error.

In your straight forward case, compiler is actually helpful to generate a warning. Treat it just as a bonus.
But look for below case where compiler doesn't recognize this problem:

int& foo ()
{
  int i = 1;
  static int j;
  return i? i : j;  // No warning in g++-5!
}

Now from the compiler perspective it's not justified if it gives error for one case and in other case it chickens out due to complexity of the code.

One of the use cases of such compiler limitation can be "Random Number generation" as nicely suggested by @tsuki.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • So in essence, situations like this are actually quite hard for the compiler to detect. – Robinson Jun 03 '15 at 11:58
  • @Robinson, assume that it's easy to detect. Still compiler cannot judge the intention of the coder. e.g. as mentioned if the user intends to generate random numbers using such functions then it's a valid operation and compiler cannot obstruct it by generating error. – iammilind Jun 03 '15 at 12:01
  • 3
    Have you actually turned warnings on? GCC does emit a `return-local-addr` warning for this exact case. – sjdowling Jun 03 '15 at 12:14
  • I find the use of this to generate random numbers very unconvincing though. I mean it's an example of use yes but when I think about the potential for bad and/or buggy code I can't imagine a standards committee letting it through for reasons like that. – Robinson Jun 03 '15 at 12:32
  • 1
    @Robinson, I don't say that committee has cited random number use case as a reason. Compiler may not generate error even for sure shot crashes like `(A*)(0)->foo()`. That doesn't mean committee approves it, but just that it is not compiler's responsibility. – iammilind Jun 03 '15 at 13:30