When I create an object using copy constructor syntax, giving it other object of that type and (crucial) the same name as the object just being constructed... the compiler treats that "other" object as the object being constructed(!) Similar effects I get with the builtin data types. Below is the short program demonstrating this (IMO) weird behavior with associated embedded Makefile and two sample outputs (#if 0--commented). I couldn't find anything about this issue in the standard (draft), but thinking about it deeper since yesterday I couldn't discover any reasonable *application* of such a semantic assuming it was valid. Similar effects with builtin data types may be a dangerous trap for programmers. At least it's very unsafe. Even if g++ gives at last warnings, they appear only when I've added (sic!) -O1, -O2... options which should have had nothing with the problem encountered (note that in all my tests I tend to use -Wall -Wextra -pedantic -ansi as a basis). On the other hand, in oppose to semantics/behavior I observe with g++, I have the strong rationale for semantics I expect (in case the standard says nothing or leave it up to compiler implementators). I get the problem with 4.1.1 on Solaris, with 3.4.4 on Cygwin as well, so I think it might be quite general problem (if not solved yet). Regards, WR. code snippet: // premature-hiding.cpp // SUBJECT: // Premature hiding of symbols makes copy constructor shooting at its own foot. // SYNOPSIS: // void fun (Seq& seq) // <- (o)uter symbol // { // Seq seq(seq); // symbols legend: (i)nner, (o)uter // // ^i ^o // **EXPECTED** // // ^i ^i // *G++ACTUAL** :( // ... // } #include <iostream> using namespace std; class X { public: X (); // In my original application I intentionally use non-const argument, // but here I put const variant, because: // 1) both give the same result, and // 2) const one looks more severe, as usually assiociated with "copying". explicit X (const X& rhs); ~X (); private: int dummy; }; inline X::X () { cout << "this ==> " << this << endl; } inline X::X (const X& rhs) { cout << "&rhs ++> " << &rhs << endl; cout << "this oo> " << this << endl; } X::~X () { cout << "~obj ~~> " << this << endl; } int main () { // Weird behavior is observed already // when dealing with simple builtin types. cout << endl << "// int n = n + 1;" << endl; int n = n + 1; cout << "n ==> " << n << endl; cout << endl << "// double r = r + 1000.0;" << endl; double r = r + 1000.0; cout << "r ==> " << r << endl; // // auto object(s) // cout << endl << "// X obj;" << endl; { // Object 'obj' defined. It should be visible by code that follows. X obj; cout << "// X obj(obj);" << endl; { // Object of the same name defined in nested scope // is expected to be properly "chained" with the parent object // of the outer scope. // Unfortunately it doesn't happen :( and GCC // allows an object being constructed to "copy" over itself. // It looks the symbol is introduced to symbol table // on '(' paren, but should be on ')'. // The latter (IMO proper) approach should only compile // if there is a symbol of that name already defined // somewhere earlier (not necessarily in nested {} as in my example) // and lexically visible. X obj(obj); // <***** UNEXPECTED BEHAVIOR cout << "// X obj(obj);" << endl; { // Trying the same on the next (3rd in turn) level... X obj(obj); // <***** UNEXPECTED BEHAVIOR (as above) } } } // // heap object(s) // cout << endl << "// X *ptr = new X(*ptr);" << endl; X *ptr = new X(*ptr); delete ptr; return 0; } #if 0 // ##### Makefile ################################################## CXX = g++ CXXFLAGS = -g -Wall -Wextra -pedantic -ansi $(EXTFLAGS) program := premature-hiding $(program): $(program).cpp Makefile $(CXX) -o $@ $(CXXFLAGS) $< run: $(program) @./$(program) clean: -rm $(program) $(program).exe *.o *.s *.ii #endif #if 0 // ##### examining flags ########################################### make -B CXXFLAGS= # clean compile, initial surprise make -B # clean too(!!), big surprise make -B EXTFLAGS='-O1' # warnings, but compiles (why it has anything with -O1 ?!) #endif #if 0 // ##### g++ (GCC) 4.1.1 [sun4u] ################################### // int n = n + 1; n ==> 67481 // double r = r + 1000.0; r ==> 1.96794e+307 // X obj; this ==> 0xffbfefc8 // X obj(obj); &rhs ++> 0xffbfefc4 <****** UNEXPECTED this oo> 0xffbfefc4 // X obj(obj); &rhs ++> 0xffbfefc0 <****** UNEXPECTED this oo> 0xffbfefc0 ~obj ~~> 0xffbfefc0 ~obj ~~> 0xffbfefc4 ~obj ~~> 0xffbfefc8 // X *ptr = new X(*ptr); &rhs ++> 0 this oo> 0x214d8 ~obj ~~> 0x214d8 #endif #if 0 // ##### g++ (GCC) 3.4.4 [cygwin] ################################## // int n = n + 1; n ==> 1628302664 // double r = r + 1000.0; r ==> 1000 // X obj; this ==> 0x22ccb0 // X obj(obj); &rhs ++> 0x22cca0 <****** UNEXPECTED this oo> 0x22cca0 // X obj(obj); &rhs ++> 0x22cc90 <****** UNEXPECTED this oo> 0x22cc90 ~obj ~~> 0x22cc90 ~obj ~~> 0x22cca0 ~obj ~~> 0x22ccb0 // X *ptr = new X(*ptr); &rhs ++> 0x4 this oo> 0x6c0768 ~obj ~~> 0x6c0768 #endif
*** Bug 31961 has been marked as a duplicate of this bug. ***
Initializer syntax int i = i; is a gcc extension. You may want to read the documentation of -Winit-self. W.
[3 weeks went by, but i've not forgotten about my bug report ;] as about the last comment (#2) I can't fully agree. -Winit-self works, but only in case of builtin scalar data types. For objects the compiler is almost quiet. Please don't misunderstand me: I don't really want any options to signal a warning on self-initializers (more options, much headache). I really expect well defined, desirable semantics for such constructs because there are cases they are very helpful. Of course, if the current standard says nothing about this or leave up to implementors, and we want to have G++ semantics here defined, there is a room for some options in the meantime until the standard becomes definite. Please note that scripting languages are well defined here. Below is a simple Perl code (strictness checks if vars are defined, while -w option add extra warning level). Regards, WR. $ perl -Mstrict -e 'my $z = 1; my $z = $z + 1; print "$z\n"' 2 $ perl -w -Mstrict -e 'my $z = 1; my $z = $z + 1; print "$z\n"' "my" variable $z masks earlier declaration in same scope at -e line 1. 2
> we want to have G++ semantics here defined The semantics are defined, just you are invoking other undefined behavior with respect of doing stuff like: b a; a = a; Which are what the semantics of C++ says the two are equaivant.