2014-11-05 9 views
13

Więc trochę kodu C++:GCC 4.8 z GNU STL generuje zły kod dla konstruktora std :: string?

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

który kompiluje grzywny z GCC 4.8 i VS 2013. Z mojej znajomości języka C++ kod jest w porządku, a zmienna lokalna theString jest wprowadzana w zakres, który następnie ukrywa theString z argumentu funkcji. W przypadku konstrukcji theString jedynym zakresem jest theString argument funkcji przekazywany do konstruktora std::string. Skonstruowana nazwa std::string jest następnie nazywana theString, która wchodzi w zakres i jest używana w dalszej części kodu. Uff!

Jednak GCC wydaje się działać jak theString przekazany do konstruktora std::string jest lokalnym theString (który nie został jeszcze zbudowany) powodując skompilowany program do wypadku. W VS 2013 kod kompiluje i działa dobrze.

Więc

  1. Czy mój kod jest prawidłowy? Czy robię coś poza specyfikacją, co oznacza, że ​​zachowanie GCC jest niezdefiniowane.
  2. Czy to błąd w GCC?
+4

Przynajmniej twój kod jest okropny. Nigdy nie należy tak kodować, ponieważ jest nieczytelne (dla ludzi). –

+15

1. Nie 2. Nie. Inicjujesz ciąg znaków z jego (niezainicjowanym) ja. – juanchopanza

+0

Tak więc błąd jest bardziej prawdopodobny w VS2013, jeśli taki istnieje. Ale twój kod jest błędny. –

Odpowiedz

30

Nie, Twój kod jest nieważny.

Według C++ Standard (3.3.2 Punkt deklaracji)

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 ]

I (3.3.3 Blok zakres, # 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.

+2

Pytania kanoniczne dla tego przykładu w "3.3.2" to: dla C++ 11 [Czy inicjalizacja pociąga za sobą konwersję l-do-wartości? Czy 'int x = x;' UB?] (Http://stackoverflow.com/q/14935722/1708801) i dla C++ 14 [Ma standard C++ zmieniony w odniesieniu do użycia nieokreślonych wartości i niezdefiniowanego zachowania w C++ 1y?] (Http://stackoverflow.com/q/23415661/1708801) –

14

To jest . paxdiablo oznacza standard C++ 03:

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.

2

Chociaż aktualne odpowiedzi są w zasadzie poprawne, to jest niezdefiniowane zachowanie, ponieważ używasz nieokreślonej wartości, szczegóły są nieco bardziej zaangażowane. W przypadku typów pierwotnych uważam, że albo Does initialization entail lvalue-to-rvalue conversion? Is int x = x; UB? lub Has C++ standard changed with respect to the use of indeterminate values and undefined behavior in C++1y? zapewniają wystarczającą szczegółowość, aby zrozumieć, dlaczego zachowanie jest niezdefiniowane.

Dla typu zdefiniowanego przez użytkownika nie wydaje mi się, aby zawierał on wystarczająco dużo szczegółów. Widzimy z tego dość stary, ale istotne defect report 63: Initialization of class from self który pyta:

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

i stanowi przykład, w którym jedynie odniesienie i adres jest podjętą klasy w trakcie budowy, a odpowiedź mówi:

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.

Odwołuje się do sekcji 3.8Czas życia obiektu, ponieważ obiekt jest w trakcie budowy, a jego pamięć została przydzielona, ​​ale jego żywotność nie rozpoczęła się, ponieważ jego inicjalizacja nie została zakończona.

Jeśli spojrzymy w pkt 6 z sekcji 3.8 mówi (podkr):

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:

i obejmuje:

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

Więc zanim zakończeniu inicjowania nie możemy podjąć wartość niestatycznego elementu danych, który wyraźnie będzie wymagany podczas kopiowania konstrukcji urządzenia std::string.