Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC 4.8 with GNU STL produces bad code for std::string constructor?

So a bit of C++ code:

void func( const std::string& theString )
{
    std::string theString( theString );
    theString += " more string";
    std::cout << theString;
}

which compiles fine with GCC 4.8 and VS 2013. From my C++ knowledge, the code is okay with a local variable theString being brought into scope which then hides theString from the function argument. At the point of theString construction, the only theString in scope is the function argument which is passed to the std::string constructor. The constructed std::string is then named theString which comes into scope and is theString used later in the code. Phew!

However, GCC seems to act like theString passed to the std::string constructor is the local theString (which hasn't been constructed yet) causing the compiled program to crash. With VS 2013 the code compiles and runs fine.

So,

  1. Is my code correct? Or am I doing something outside spec which means the GCC behaviour is undefined.
  2. Is this a bug in GCC?
like image 811
user2746401 Avatar asked Nov 05 '14 13:11

user2746401


3 Answers

No, your code is invalid.

According to the C++ Standard (3.3.2 Point of declaration)

1 The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), except as noted below.

[ Example:
int x = 12;
{ int x = x; }

Here the second x is initialized with its own (indeterminate) value. —end example ]

And (3.3.3 Block scope, #2)

A parameter name shall not be redeclared in the outermost block of the function definition nor in the outermost block of any handler associated with a function-try-block.

like image 87
Vlad from Moscow Avatar answered Nov 09 '22 22:11

Vlad from Moscow


This is undefined behaviour in C++. paxdiablo quotes the C++03 standard:

The point of declaration for a name is immediately after its complete declarator (clause 8) and before its initializer (if any) ...

Example:

int x = 12;
{ int x = x; }

Here the second x is initialized with its own (indeterminate) value.

like image 33
vz0 Avatar answered Nov 09 '22 21:11

vz0


Although the current answers are basically correct, this is undefined behavior because you are using an indeterminate value the details are a bit more involved. For primitive types I believe either Does initialization entail lvalue-to-rvalue conversion? Is int x = x; UB? or Has C++ standard changed with respect to the use of indeterminate values and undefined behavior in C++1y? provide sufficient detail to understand why the behavior is undefined.

For a user defined type though I don't think they fill in enough of the details. We can see from this rather old but relevant defect report 63: Initialization of class from self which asks:

And if so, what is the semantics of the self-initialization of UDT?

and provides an example in which only the reference and the address is taken of the class under construction and the answer says:

3.8 basic.life paragraph 6 indicates that the references here are valid. It's permitted to take the address of a class object before it is fully initialized, and it's permitted to pass it as an argument to a reference parameter as long as the reference can bind directly.

It is referring to section 3.8 Object lifetime because the object is under construction and its storage has been allocated but it lifetime has not began because its initialization is not complete.

If we look at paragraph 6 from section 3.8 it says (emphasis mine):

Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see 12.7. Otherwise, such a glvalue refers to allocated storage (3.7.4.2), and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if:

and includes:

the glvalue is used to access a non-static data member or call a non-static member function of the object,

So before the initialization is complete we can not take the value of a non-static data member which clearly will be required during copy construction of a std::string.

like image 2
Shafik Yaghmour Avatar answered Nov 09 '22 21:11

Shafik Yaghmour