 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
seanf Guest
|
Posted: Fri Dec 08, 2006 10:10 am Post subject: name lookup and unnamed namespaces |
|
|
Hi everyone
This code fails to compile with gcc 3.4.6 & msvc++7:
(can't find operator<<)
--------8<------------
#include <ostream>
#include <iostream>
#include <iterator>
using namespace std;
class C {};
namespace
{
ostream& operator<<(ostream& o, const C&)
{
return o;
}
}
int main()
{
ostream_iterator<C> i(cout, "\n");
i = C();
}
--------8<------------
However, it works if I put the operator<< in the global namespace.
What's going on here?
While trying to distil a minimal example, I found that this compiles:
--------8<------------
namespace N
{
template <class T> struct K
{
void f(const T& t) { g(t); }
};
}
using namespace N;
class C {};
namespace
{
void g(const C&) {}
}
int main()
{
K<C> k;
k.f(C());
}
--------8<------------
I can't seem to bridge the gap between the two examples. What am I
overlooking?
TIA (and thanks for help with my previous problems)
Sean
--
temporary email: cppthrowaway at blueyonder dot co dot uk
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Sat Dec 09, 2006 4:01 am Post subject: Re: name lookup and unnamed namespaces |
|
|
seanf wrote:
| Quote: | This code fails to compile with gcc 3.4.6 & msvc++7:
(can't find operator<<)
--------8<------------
#include <ostream
#include <iostream
#include <iterator
using namespace std;
class C {};
namespace
{
ostream& operator<<(ostream& o, const C&)
{
return o;
}
}
int main()
{
ostream_iterator<C> i(cout, "\n");
i = C();
}
--------8<------------
However, it works if I put the operator<< in the global namespace.
What's going on here?
|
The use of the operator<< is in the template code for
ostream_iterator, not in your code. In this case, the
operator<< will doubtlessly be a dependant name lookup (he sais,
without even looking at the source code). Dependant name lookup
does the lookup in two separate contexts: the context where the
template was defined (and it can never find your operator
there), and using ADL in the instantiation context. The fact
that it uses ADL only in the instantiation context means that
only names in a dependant namespace of one of the arguments can
be found: here in std (because of the std::ostream argument) and
in the global namespace (because of your class C). Since your
operator is in neither, it isn't found.
| Quote: | While trying to distil a minimal example, I found that this compiles:
--------8<------------
namespace N
{
template <class T> struct K
{
void f(const T& t) { g(t); }
};
}
using namespace N;
class C {};
namespace
{
void g(const C&) {}
}
int main()
{
K<C> k;
k.f(C());
}
--------8<------------
I can't seem to bridge the gap between the two examples. What am I
overlooking?
|
The fact that in this second example, the function call is not
in a template. There is only one context for lookup, which is
where the function call is written. (Think of it for a minute.
The function call in ostream_iterator is in <iterator>. Your
operator hasn't been declared at all yet.) In this context,
names found by both normal lookup and ADL are considered; normal
lookup at the point of the call finds your function, and so it
works.
--
James Kanze (GABI Software) email:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Daniel Krügler Guest
|
Posted: Sun Dec 10, 2006 9:28 am Post subject: Re: name lookup and unnamed namespaces |
|
|
seanf schrieb:
| Quote: | Yuck. But I see from 3.4.2 that I can't get C into the namespace (for
ADL purposes) with 'typedef' or 'using'.
I'm stuck with C in the global namespace, so I suppose I'll make
operator<< static in the global namespace. I know this practice is
deprecated, but I don't expect they can remove it any time soon.
|
I hope I don't misquote the standard at this place (I would appreciate
the comments of others), but I don't think that you will have more
luck using internal linkage. I interpret 14.6.4.2, which says
"For a function call that depends on a template parameter, if the
function name is an unqualified-id but not a template-id, the
candidate functions are found using the usual lookup rules (3.4.1,
3.4.2) except that:
- For the part of the lookup using unqualified name lookup (3.4.1),
only function declarations with external linkage from the template
definition context are found.
- For the part of the lookup using associated namespaces (3.4.2),
only function declarations with external linkage found in either the
template definition context or the template instantiation context are
found."
so, that
static ostream& operator()(ostream&, constC&);
should not be found.
If you really prefer only a locally valid streaming capability of
class C, you could use a "private" proxy class for this. I mean
along the lines of:
class C{}; // Global namespace
#include <ostream>
#include <iostream>
#include <iterator>
using namespace std;
namespace {
struct CProxy {
CProxy(const C& c): c(&c) {}
ostream& print(ostream& os) const {
// use os to stream member c
return os;
}
const C* c;
};
inline ostream& operator<<(ostream& os, const CProxy& p) {
return p.print(os);
}
}
int main()
{
ostream_iterator<CProxy> i(cout, "\n");
i = CProxy(C());
}
Greetings from Bremen,
Daniel Krügler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Mon Dec 11, 2006 5:20 am Post subject: Re: name lookup and unnamed namespaces |
|
|
Daniel Krügler wrote:
| Quote: | James Kanze schrieb:
|
[...]
| Quote: | The fact that in this second example, the function call is not
in a template. There is only one context for lookup, which is
where the function call is written. (Think of it for a minute.
The function call in ostream_iterator is in <iterator>. Your
operator hasn't been declared at all yet.) In this context,
names found by both normal lookup and ADL are considered; normal
lookup at the point of the call finds your function, and so it
works.
Hmmh, forgive me, but I don't understand your reasoning:
What do you mean with "the function call is not in a template"?
|
Just that I didn't look far enough into the actual code:-). I
was only looking at the call to K<>::f.
| Quote: | Let me replicate the calling point of the 2nd OP example here:
namespace N{
template <class T> struct K
{
void f(const T& t) { g(t); }
};
}
As I see it, g is called inside a member function of the
class template ::N::K. The call is under dependent lookup
conditions, because g is called unqualified with an argument
t which has type T, which is the template parameter. As far
as I see, this call fulfills all conditions of ADL.
|
Yes. The call to g() is a dependant lookup. At first glance,
the main (only?) difference I see is that there isn't a g() in
namespace N to find (and ADL wouldn't look in N anyway, I
think---but I'll be quite frank and say that I have a great deal
of trouble understanding all this, and figuring out what really
should happen).
| Quote: | Since I like to learn, I would like to present what I think
happens (or should happen), so please point out, what
is wrong in my argumentation. For easier analysis I
repeat the complete code added with some annotations:
namespace N {
template <class T> struct K {
void f(const T& t) { g(t); } // (a)
};
}
using namespace N;
class C {};
namespace {
void g(const C&) {}
}
// (b)
int main() {
K<C> k;
k.f(C());
}
The call of g at point (a) should be a dependent one,
because its argument t is of dependent on T, such we
should have all conditions for dependent lookup. Under
these conditions we should have 2 phase lookup. The
first phase uses ordinary lookup and does not find
any match here, because ordinary lookup takes everything
into account, that occurs lexically before (a). There
is nothing to find here (Note: In the case of the first
example the compiler could possibly find other overloads
of operator<< from namespace std at this stage, if these
were #included before the definition of
::std::ostream_iterator<T, ..>::operator=(const T&); But
this is an implementation-depended issue).
|
That's an interesting point you make. The results in such cases
depend on the order of user includes and whether specific system
headers include other headers or not. The user's original code
included <ostream> before including <iterator>---if we suppose
that <ostream> does not include <iterator> (although it might, I
suspect that it usually doesn't), then he is guaranteed that the
other << operators are visible when ostream_iterator is defined.
If he inverted the order of the includes, it would depend on
whether <iterator> included <ostream>; I suspect that many do
(since it is useful when defining ostream_iterator), but it
certainly isn't guaranteed.
| Quote: | Since K has no member g, it is also not found as immediate
member of K.
In the second phase we have to consider the instantiation point.
According to instantiation rules, the POI of K<C> should occur
at the point (b). In this phase *only* ADL is taken into account,
|
I think that's misleading. Because the name is dependant,
lookup occurs in phase 2, and phase 2 in fact uses two separate
lookups: one at the point of definition (in the header file),
and the second at the point of instantiation. It is only in the
second that only ADL is taken into account.
| Quote: | that means, because C is of class type, that according to
3.4.2 p. 2:
- "its associated classes are: the class itself; the class of
which it is a member, if any; and its direct and indirect base
classes. Its associated namespaces are the namespaces
in which its associated classes are defined."
According to the following para 2a:
"If the ordinary unqualified lookup of the name finds the declaration
of a class member function, the associated namespaces and
classes are not considered."
|
I'm not sure how this applies here. If I define a class and its
operator<< in a namespace, e.g.:
namespace N {
class C {} ;
std::ostream& operator<<( std::ostream&, C const& ) ;
}
it will be found in std::ostream_iterator. (At least, I
certainly hope so.) Even though ordinary unqualified lookup at
the point of definition finds a number operator<< (remember, the
definition is in namespace std::, so ordinary lookup looks
there), and this operator can only be found by ADL in the lookup
at the point of instantiation.
| Quote: | we have to proceed with the seach, because there is no
function member ::N::K<..>::g found. These rules are explained as
"Otherwise the set of declarations found by the lookup of the
function name is the union of the set of declarations found using
ordinary unqualified lookup and the set of declarations found
in the namespaces and classes associated with the argument types."
The set of declarations found via ordinary lookup was empty.
C is defined in the global namespace, where we do not have
any ::g().
There indeed exists a function g() inside an unnamed
namespace, which should behave according to 7.3.1.1 p. 1
as-if a using directive would exist:
namespace "some_unique_identifier" {}
using namespace "some_unique_identifier";
namespace "some_unique_identifier" {
void g(const C&){}
}
Although at point (b) the function "some_unique_identifier"::g
*could* be seen (e.g. under the rules of ordinary lookup), this
case is explicitly exluded from ADL via 3.4.2 p 3:
"Any using-directives in the associated namespace are ignored."
At this point I don't see, how g could be found in this example.
|
I don't either:-). But I don't claim to understand this very
well. The difference is obviously (?) due to the fact that the
unqualified lookup at the point of definition finds function
definitions in one case, and that it doesn't in the other.
Quite frankly, I'm completely lost.
--
James Kanze (Gabi Software) email: james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|