Dependent name resolution & namespace std / Standard Library
Asked Answered
A

1

6

While answering this SO question (better read this "duplicate"), I came up with the following solution to dependent name resolution of an operator:

[temp.dep.res]/1:

In resolving dependent names, names from the following sources are considered:

  • Declarations that are visible at the point of definition of the template.
  • Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.
#include <iostream>
#include <utility>

// this operator should be called from inside `istream_iterator`
std::istream& operator>>(std::istream& s, std::pair<int,int>& p)
{
    s >> p.first >> p.second;
    return s;
}

// include definition of `istream_iterator` only after declaring the operator
// -> temp.dep.res/1 bullet 1 applies??
#include <iterator>

#include <map>
#include <fstream>

int main()
{
    std::ifstream in("file.in");

    std::map<int, int> pp; 
    pp.insert( std::istream_iterator<std::pair<int, int>>{in},
               std::istream_iterator<std::pair<int, int>>{} );
}

But clang++ 3.2 and g++ 4.8 don't find this operator (name resolution).

Doesn't the inclusion of <iterator> define the "point of definition of the template" istream_iterator?

Edit: As Andy Prowl points out, this has nothing to do with the Standard Library, but rather with name lookup (can be proven by mimicking the Standard Library with multiple operator>>, at least one in the namespace of the fake istream).


Edit2: A workaround, using [basic.lookup.argdep]/2 bullet 2

#include <iostream>
#include <utility>

// can include <iterator> already here,
// as the definition of a class template member function
// is only instantiated when the function is called (or explicit instantiation)
// (make sure there are no relevant instantiations before the definition
//  of the operator>> below)
#include <iterator>

struct my_int
{
    int m;
    my_int() : m() {}
    my_int(int p) : m(p) {}
    operator int() const { return m; }
};

// this operator should be called from inside `istream_iterator`
std::istream& operator>>(std::istream& s, std::pair<my_int,my_int>& p)
{
    s >> p.first.m >> p.second.m;
    return s;
}

#include <map>
#include <fstream>

int main()
{
    std::ifstream in("file.in");

    std::map<int, int> pp; 
    pp.insert( std::istream_iterator<std::pair<my_int, my_int>>{in},
               std::istream_iterator<std::pair<my_int, my_int>>{} );
}

Of course, you can also use your own pair type, as long as the workaround introduces an associated class in the namespace of the custom operator>>.

Anticipative answered 14/5, 2013 at 16:32 Comment(0)
M
4

The problem here is that the point where your call to operator >> is being made is somewhere inside the std namespace, and the namespace where the types of the arguments live is std.

Provided the compiler can find an operator >> in either the namespace where the call occurs or the namespace where the types of the arguments live (both are the std namespace in this case), no matter whether it is viable or not for overload resolution (which is performed after name lookup), it won't bother looking for more overloads of operator >> in parent namespaces.

Unfortunately, your operator >> lives in the global namespace and is, therefore, not found.

Monochord answered 14/5, 2013 at 16:40 Comment(4)
Could you provide a reference please? :) I'm eager to look that up in the StandardAnticipative
Ok I got it :) [basic.lookup.unqual]/1; lookup in associated namespaces/argument-dependent lookup doesn't work here as both types are from namespace std.Anticipative
AFAIK the only associated namespace is namespace std, and I don't want to inject the operator in there.Anticipative
@DyP: Oh, OK, I misunderstood your sentence thenMonochord

© 2022 - 2024 — McMap. All rights reserved.