According to 5.2.2/4 "Function call" in n4640 (8.2.2/4 in n4659) function parameters are created and destroyed in the context of the caller. And implementations are allowed to delay the destruction of function parameters to the end of the enclosing full expression (as an implementation-defined feature). Note that the choice is not unspecified, but rather implementation-defined.
(It is not entirely clear how this agrees with 3.3.3 "Block scope" (6.3.3 in n4659), which seems to imply that function parameters have block scope, and then 3.7.3 "Automatic storage duration" (6.7.3 in n4659), which says that the storage for block scope variables lasts until the block in which they are created exits. But let's assume that I'm missing/misunderstanding something in the wording. Apparently now function parameters will have their own scope)
As far as I know, ABI requires GCC and Clang to delay the destruction of function parameters to the end of full expression, i.e. this is the implementation-defined behavior of these compilers. I would guess that in implementations like that it should be OK to return references/pointers to function parameters as long as these references/pointers are used within the calling expression only.
However, the following example segfaults in GCC and works fine in Clang
#include <iostream>
#include <string>
std::string &foo(std::string s)
{
return s;
}
int main()
{
std::cout << foo("Hello World!") << std::endl;
}
Both compilers issue a warning about returning a reference to a local variable, which is perfectly appropriate here. A quick inspection of the generated code shows that both compilers do indeed delay the destruction of the parameter to the end of the expression. However, GCC still deliberately returns a "null reference" from foo
, which causes the crash. Meanwhile, Clang behaves "as expected", returning a reference to its parameter s
, which survives long enough to produce the expected output.
(GCC is easy to fool in this case by simply doing
std::string &foo(std::string s)
{
std::string *p = &s;
return *p;
}
which fixes the segfault under GCC.)
Is GCC's behavior justified in this case, under assumption that it guarantees "late" destruction of parameters? Am I missing some other passage in the standard that says that returning references to function parameters is always undefined, even if their lifetimes are extended by the implementation?