sdm@cs.brown.edu (Scott Meyers) (01/16/90)
Consider the following straightforward program for reading in enumerated type values: [ 1] #include <iostream.h> [ 2] #include <string.h> [ 3] [ 4] enum Colors { RED, BLUE, GREEN }; [ 5] [ 6] istream &operator >> ( istream &s, Colors &c ) [ 7] { [ 8] char string[20]; [ 9] s >> string; [10] if ( strcmp( string, "RED" ) == 0) c = RED; [11] else if ( strcmp( string, "GREEN" ) == 0 ) c = GREEN; [12] else if ( strcmp( string, "BLUE" ) == 0 ) c = BLUE; [13] else cerr << "Error reading input"; [14] [25] return s; [16] } [17] [18] main() [19] { [20] Colors color; [21] cin >> color; [22] cout << "color = " << color << "\n"; [23] } This worked fine under CC 1.2, and it continues to work fine with g++, but it fails to work with the CC 2.0 iostream library. The problem is that the "wrong" operator>> is being called in line 21. cin is not of type istream, but of type istream_withassign, so line 21 wants to match a function with a signature of operator>>(istream_withassign, Colors). There is no exact match for such a signature, so cfront considers other possibilities. The two relevant ones are operator>>(istream, Colors) // the "right" one operator>>(istream_withassign, int) // the "wrong" one (from istream.h) From p. 88 of the C++ Reference Manual, we find the following rules for argument matching (to be applied in this order): [1] Exact match: sequences of zero or more trivial convertions are better than all other sequences. [2] Match with promotions: of sequences not mentioned in [1], those that contain integral promotions [and some others] are better than all others. [3] Match with standard convertions: of sequences not mentioned in [2], those with only standard and trivial convertions are better than all others. [Derived to base convertions are standard convertions.] [4] Match with convertions requiring temporaries. [5] Match with user-defined convertions. The "wrong" function is chosen because it can be matched by applying rule [2]: promotion of an enum to an int. To choose the "right" function would call for an application of rule [3]: standard conversion of a derived class (istream_withassign) to a base class (istream). The program can be fixed by redeclaring operator>> as follows: istream &operator >> ( istream_withassign &s, Colors &c ) This strikes me as needlessly complex: why should I have to know about the details of the istream library in order to do something simple like read in enumerated type values? It is also asymmetrical with outputting enum values, since ostreams work perfectly fine there. Is there a better way to approach this problem? (This issue arose in the context of teaching students C++. We taught them enums early on, before they knew about classes, and in 1.2 everything fit together nicely. With the change in the iostream library, input of enums is no longer trivial. We'd like to make it trivial again :-). For the record, Moises Lejter was responsible for figuring out why the above program didn't work.) Scott