Title
Operator lookup rules do not work well with parts of the library
Status
nad
Section
12.2.2.3 [over.match.oper]
Submitter
Herb Sutter

Created on 1998-10-15.00:00:00 last changed 301 months ago

Messages

Date: 1999-10-15.00:00:00

Rationale (10/99): This appears to be mainly a program design issue. Furthermore, any attempt to address it in the core language would be beyond the scope of what can be done in a Technical Corrigendum.

Date: 2022-02-18.07:47:23

The following example does not work as one might expect:

    namespace N { class C {}; }
    int operator +(int i, N::C) { return i+1; }

    #include <numeric>
    int main() {
        N::C a[10];
        std::accumulate(a, a+10, 0);
    }
According to 6.5.3 [basic.lookup.unqual] paragraph 6, I would expect that the "+" call inside std::accumulate would find the global operator+. Is this true, or am I missing a rule? Clearly, the operator+ would be found by Koenig lookup if it were in namespace N.

Daveed Vandevoorde: But doesn't unqualified lookup of the operator+ in the definition of std::accumulate proceed in the namespace where the implicit specialization is generated; i.e., in namespace std?

In that case, you may find a non-empty overload set for operator+ in namespace std and the surrounding (global) namespace is no longer considered?

Nathan Myers: Indeed, <string> defines operator+, as do <complex>, <valarray>, and <iterator>. Any of these might hide the global operator.

Herb Sutter: These examples fail for the same reason:

    struct Value { int i; };

    typedef map<int, Value > CMap;
    typedef CMap::value_type CPair;

    ostream & operator<< ( ostream &os, const CPair &cp )
      { return os << cp.first << "/" << cp.second.i; }

    int main() {
      CMap courseMap;
      copy( courseMap.begin(), courseMap.end(),
            ostream_iterator<CPair>( cout, "\n" ) );
    }

    template<class T, class S>
    ostream& operator<< (ostream& out, pair<T,S> pr)
      { return out << pr.first << " : " << pr.second << endl; }

    int main() {
      map <int, string> pl;
      copy( pl.begin(), pl.end(),
            ostream_iterator <places_t::value_type>( cout, "\n" ) );
    }
This technique (copying from a map to another container or stream) should work. If it really cannot be made to work, that would seem broken to me. The reason is does not work is that copy and pair are in namespace std and the name lookup rules do not permit the global operator<< to be found because the other operator<<'s in namespace std hide the global operator. (Aside: FWIW, I think most programmers don't realize that a typedef like CPair is actually in namespace std, and not the global namespace.)

Bill Gibbons: It looks like part of this problem is that the library is referring to names which it requires the client to declare in the global namespace (the operator names) while also declaring those names in namespace std. This would be considered very poor design for plain function names; but the operator names are special.

There is a related case in the lookup of operator conversion functions. The declaration of a conversion function in a derived class does not hide any conversion functions in a base class unless they convert to the same type. Should the same thing be done for the lookup of operator function names, e.g. should an operator name in the global namespace be visible in namespace std unless there is a matching declaration in std?

Because the operator function names are fixed, it it much more likely that a declaration in an inner namespace will accidentally hide a declaration in an outer namespace, and the two declarations are much less likely to interfere with each other if they are both visible.

The lookup rules for operator names (when used implicitly) are already quite different from those for ordinary function names. It might be worthwhile to add one more special case.

Mike Ball : The original SGI proposal said that non-transitive points of instantiation were also considered. Why, when, and by whom was it added?

Rationale (10/99): This appears to be mainly a program design issue. Furthermore, any attempt to address it in the core language would be beyond the scope of what can be done in a Technical Corrigendum.

History
Date User Action Args
2000-02-23 00:00:00adminsetmessages: + msg288
2000-02-23 00:00:00adminsetstatus: open -> nad
1998-10-15 00:00:00admincreate