Discussion:
using directive
(too old to reply)
Fraser Ross
2008-07-29 17:14:46 UTC
Permalink
namespace A {
void f(int){}
}
namespace B {
using namespace A;
void f(int i) {
f(i);
}
}

int main()
{
B::f(1);
return 0;
}

Does B::f hide A::f and therefore f(i) calls B::f? BCB2006 says its
ambiguous?


I discovered a bug which is reported as 18810. It is present with a
later build although the report says its fixed. It is produced with the
following:

namespace A {}
class B{
using namespace A;
};

Fraser.
Duane Hebert
2008-07-29 17:45:45 UTC
Permalink
Post by Fraser Ross
namespace A {
void f(int){}
}
namespace B {
using namespace A;
void f(int i) {
f(i);
}
}
int main()
{
B::f(1);
return 0;
}
Does B::f hide A::f and therefore f(i) calls B::f? BCB2006 says its
ambiguous?
I don't think it's actually a question of hiding since you bring A::f() into
scope with the using declaration.

With MSVC2005 I get no compiler diagnostics but a linker warning:

warning C4717: 'B::f' : recursive on all control paths, function will cause
runtime stack overflow
which is sort of expected I think.

Comeau's online compiler also has no problem compiling this but it doesn't
link.

I'm not sure why BCB thinks that it's ambiguous. I imagine that you're
asking
about the unqualified call to f(int) inside of B::f(int). I'm not sure what
the rule is here
but it seems that VS is choosing B::f(int) .
Fraser Ross
2008-07-29 17:59:56 UTC
Permalink
"Duane Hebert"
Post by Duane Hebert
I don't think it's actually a question of hiding since you bring A::f() into
scope with the using declaration.
Its a using directive.
Post by Duane Hebert
I'm not sure why BCB thinks that it's ambiguous. I imagine that you're
asking
about the unqualified call to f(int) inside of B::f(int). I'm not sure what
the rule is here
but it seems that VS is choosing B::f(int) .
Comeau is probably resolving to B::f(int) as well.

Fraser.
Duane Hebert
2008-07-29 18:14:36 UTC
Permalink
Post by Fraser Ross
"Duane Hebert"
Post by Duane Hebert
I don't think it's actually a question of hiding since you bring
A::f() into
Post by Duane Hebert
scope with the using declaration.
Its a using directive.
Ok. But it still is bringing A::f() into scope so it
isn't a question of hiding IMO.
Post by Fraser Ross
Post by Duane Hebert
I'm not sure why BCB thinks that it's ambiguous. I imagine that
you're
Post by Duane Hebert
asking
about the unqualified call to f(int) inside of B::f(int). I'm not
sure what
Post by Duane Hebert
the rule is here
but it seems that VS is choosing B::f(int) .
Comeau is probably resolving to B::f(int) as well.
Yep. It seems to see the choices as A::f() and f()
which sort of makes sense but I don't have the standard
handy so I don't know if it's defined behavior. I would guess
that since both MSVC8 and Comeau agree, the chances are
good.
Fraser Ross
2008-07-29 18:22:50 UTC
Permalink
"Duane Hebert"
Post by Duane Hebert
Ok. But it still is bringing A::f() into scope so it
isn't a question of hiding IMO.
That theory could be wrong then. Is the name of a function injected
with its body, similar to class name injection?

Fraser.
Chris Uzdavinis (TeamB)
2008-07-29 18:51:47 UTC
Permalink
Post by Fraser Ross
"Duane Hebert"
Post by Duane Hebert
Ok. But it still is bringing A::f() into scope so it
isn't a question of hiding IMO.
That theory could be wrong then. Is the name of a function injected
with its body, similar to class name injection?
Actually, this behavior is because namespace A contains the name f
directly. When you qualify a name (as in A::f), the using directives
in that namespace are entirely ignored.

If the namespace does not contain the name directly, then the
transitive closure of all the using directives are considered.

To extend upon this, if you have a namespace 'A' that uses another
namespace 'B', and 'B' directly contains the searched-for name and
also uses namespace 'C', then the using directive in B is ignored.

So if you think of a namespace as a tree (when considering its using
directives for other namespaces), any namespace that directly provides
a name is a leaf node in the tree.
--
Chris (TeamB);
Duane Hebert
2008-07-29 18:54:17 UTC
Permalink
Post by Fraser Ross
"Duane Hebert"
Post by Duane Hebert
Ok. But it still is bringing A::f() into scope so it
isn't a question of hiding IMO.
That theory could be wrong then. Is the name of a function injected
with its body, similar to class name injection?
Good question and I'm not sure. But in a typical inheritance scheme,
if the base class has a function like void f(double) and the derived
class wants to overload it with void f(int) it doesn't work unless
you bring the base ::f(double) into scope with a using directive.
Otherwise, it hides the base implementation. (I think BCB gives a
diagnostic in that case),

I guess you know this but anyway, I think it's the same idea
as in your example.

I think that in the scope of the function call
in B::f(int), when it sees the f(1) it has a choice of A::f(int) and f(int)
(since B::f() is local). It matches the local one.

Someone more versed in the standard can clarify but in the case
of MSVC it's definitely choosing this one because if I run it I can see
the endless recursion.

I'd like an expert explanation because this is one section of the
language that I find the most confusing. Though it normally comes
up only on interview questions <g>
Fraser Ross
2008-07-30 07:06:04 UTC
Permalink
7.3.4/3 has an example that I think demonstrates the same thing:

namespace A {
int i;
namespace B {
namespace C {
int i;
}
using namespace A::B::C;
void f1() {
i = 5; // OK, C::i visible in B and hides A::i
}
}
}

The using directive makes A::i hidden. In my program the function f
hides the other function f that was made visible by the using directive.

Fraser.
Fraser Ross
2008-07-30 07:27:46 UTC
Permalink
"Fraser Ross"
Post by Fraser Ross
In my program the function f
hides the other function f that was made visible by the using
directive.

Any overloads of f in A also appear to be hidden.

Fraser.
Duane Hebert
2008-07-30 13:04:56 UTC
Permalink
Post by Fraser Ross
namespace A {
int i;
namespace B {
namespace C {
int i;
}
using namespace A::B::C;
void f1() {
i = 5; // OK, C::i visible in B and hides A::i
}
}
}
The using directive makes A::i hidden. In my program the function f
hides the other function f that was made visible by the using directive.
These namespaces are nested and your using directive
is referring to the innermost namespace which is the one
where you are.

Here, your telling it that you're using C::
In this case, if you had declared f1() in A it
would probably hide it.

Doesn't seem like the same thing as your original
post. In your original, you had two separate namespaces.
In B you told it to use namespace A.
Duane Hebert
2008-07-30 13:15:44 UTC
Permalink
Post by Fraser Ross
The using directive makes A::i hidden. In my program the function f
hides the other function f that was made visible by the using directive.
Sorry, I didn't read the last line correctly. I see what you mean now. The
function A::f() is not seen when calling it by f() alone. But you can still
call A::f() explicitly. I guess you could say that A::f() is hidden.

I'm not used to that terminology for classes
or namespaces that are separate. Hiding a function usually comes up when
deriving from a base and reimplementing a non virtual base function and
this is usually a design error.

Anyway, it sounds like BCB is incorrect with the ambiguous
diagnostic.
Vaclav Cechura
2008-07-30 13:46:25 UTC
Permalink
Post by Fraser Ross
Does B::f hide A::f and therefore f(i) calls B::f? BCB2006 says its
ambiguous?
"If a function declaration in namespace scope or block scope
has the same name and the same parameter types as a function
introduced by a using-declaration, and the declarations do not
declare the same function, the program is ill-formed."

If I understand this part of the C++ Standard right,
the program is ill formed.

This is the whole paragraph:

"7.3.3 The using declaration
If a function declaration in namespace scope or block scope has
the same name and the same parameter types as a function
introduced by a using-declaration, and the declarations do not
declare the same function, the program is ill-formed. [Note:
two using-declarations may introduce functions with the same
name and the same parameter types. If, for a call to an
unqualified function name, function overload resolution
selects the functions introduced by such using-declarations,
the function call is ill-formed.
[Example:
namespace B {
void f(int);
void f(double);
}
namespace C {
void f(int);
void f(double);
void f(char);
}
void h()
{
using B::f; // B::f(int) and B::f(double)
using C::f; // C::f(int), C::f(double), and C::f(char)
f(’h’); //calls C::f(char)
f(1); //error: ambiguous: B::f(int) or C::f(int) ?
void f(int); // error:
// f(int) conflicts with C::f(int) and B::f(int)
}
—end example] ]"

Vaclav
Alan Bellingham
2008-07-30 14:31:58 UTC
Permalink
Post by Vaclav Cechura
"If a function declaration in namespace scope or block scope
has the same name and the same parameter types as a function
introduced by a using-declaration, and the declarations do not
declare the same function, the program is ill-formed."
If I understand this part of the C++ Standard right,
the program is ill formed.
Yes, but ... what's under discussion isn't the using declaration but the
using directive, which is subtly different.

7.3.4/5:

5 During overload resolution, all functions from the transitive search
are considered for argument matching. The set of declarations found
by the transitive search is unordered. [Note: in particular, the
order in which namespaces were considered and the relationships among
the namespaces implied by the using-directives do not cause
preference to be given to any of the declarations found by the
search. ] An ambiguity exists if the best match finds two functions
with the same signature, even if one is in a namespace reachable
through using-directives in the namespace of the other.84)

The last sentence appears to apply. In the case asked about, we do find
two functions with the same signature, and one of them is in a namespace
reachable through the using directive within the other.

Alan Bellingham
--
Team Browns
ACCU Conference 2009: to be announced
Duane Hebert
2008-07-30 15:10:26 UTC
Permalink
5 During overload resolution, all functions from the transitive search
are considered for argument matching. The set of declarations found
by the transitive search is unordered. [Note: in particular, the
order in which namespaces were considered and the relationships among
the namespaces implied by the using-directives do not cause
preference to be given to any of the declarations found by the
search. ] An ambiguity exists if the best match finds two functions
with the same signature, even if one is in a namespace reachable
through using-directives in the namespace of the other.84)
The last sentence appears to apply. In the case asked about, we do find
two functions with the same signature, and one of them is in a namespace
reachable through the using directive within the other.
Alan Bellingham
Given that then I would have to say that Borland's diagnostic
is correct and both MSVC8 and Comeau are wrong. I was assuming
that there was some rule where a function in the existing scope (not
needing namespace qualifier to be called) would take precedence
but that doesn't seem to be the case reading that last sentence.
If the local one doesn't take precedence then the call should be
ambiguous, no?
Chris Uzdavinis (TeamB)
2008-07-30 15:20:20 UTC
Permalink
Post by Duane Hebert
5 During overload resolution, all functions from the transitive search
are considered for argument matching. The set of declarations found
by the transitive search is unordered. [Note: in particular, the
order in which namespaces were considered and the relationships among
the namespaces implied by the using-directives do not cause
preference to be given to any of the declarations found by the
search. ] An ambiguity exists if the best match finds two functions
with the same signature, even if one is in a namespace reachable
through using-directives in the namespace of the other.84)
The last sentence appears to apply. In the case asked about, we do find
two functions with the same signature, and one of them is in a namespace
reachable through the using directive within the other.
Alan Bellingham
Given that then I would have to say that Borland's diagnostic
is correct and both MSVC8 and Comeau are wrong.
Hmm. The wording of the standard has changed significantly in this
section from the older copy I was reviewing. Alan, is that c++2003 or
from the new draft?

Everything I said was backed by the wording from the 1998 original
standard, which is what I happen to have on-hand right now. Given
what Alan quoted, it does look like Borland is right and the others
aren't. I think it would take a little more research to conclude
anything though, as in just about every case, when I think Comeau has
an error it usually turns out I am the one who is mistaken. It
usually ends up being a learning experience.
--
Chris (TeamB);
Alan Bellingham
2008-07-30 16:05:01 UTC
Permalink
Post by Chris Uzdavinis (TeamB)
Hmm. The wording of the standard has changed significantly in this
section from the older copy I was reviewing. Alan, is that c++2003 or
from the new draft?
Ah, good point. That's ISO/IEC 14882:1998(E), which is what I also have
on hand.

My hardcopy (ISO/IEC 14882:2003) has unchanged wording.
Post by Chris Uzdavinis (TeamB)
anything though, as in just about every case, when I think Comeau has
an error it usually turns out I am the one who is mistaken. It
usually ends up being a learning experience.
Homer nods and all that, but Greg does tend to provide the gold
standard.

Alan Bellingham
--
Team Browns
ACCU Conference 2009: to be announced
Alan Bellingham
2008-07-30 17:06:09 UTC
Permalink
Given X::m (where X is a user-declared namespace), [...], let S be
the set of all declarations of m in X and in the transitive closure
of all namespaces nominated by using-directives in X and its used
namespaces, EXCEPT THAT USING-DIRECTIVES ARE IGNORED IN ANY
NAMESPACE, INCLUDING X, DIRECTLY CONTAINING ONE OR MORE DECLARATIONS
OF m.
(emphasis added. Again citing 1998 standard which may have possibly
changed in the last ... decade.)
Ah, right. Yes, that makes sense. It does make code slightly less prone
to falling over in a big smelly heap when a new declaration appears
several namespaces away that happens to be the same as in the namespace
you're working in.

Namespaces are hard and tricksy and nothing like as simple as naive
users think.

Alan Bellingham
--
Team Browns
ACCU Conference 2009: to be announced
Duane Hebert
2008-07-30 17:09:49 UTC
Permalink
Post by Alan Bellingham
Given X::m (where X is a user-declared namespace), [...], let S be
the set of all declarations of m in X and in the transitive closure
of all namespaces nominated by using-directives in X and its used
namespaces, EXCEPT THAT USING-DIRECTIVES ARE IGNORED IN ANY
NAMESPACE, INCLUDING X, DIRECTLY CONTAINING ONE OR MORE DECLARATIONS
OF m.
(emphasis added. Again citing 1998 standard which may have possibly
changed in the last ... decade.)
Ah, right. Yes, that makes sense. It does make code slightly less prone
to falling over in a big smelly heap when a new declaration appears
several namespaces away that happens to be the same as in the namespace
you're working in.
Namespaces are hard and tricksy and nothing like as simple as naive
users think.
Don't know. My original guess was sort of naive and close.
Hendrik Schober
2008-08-01 17:03:13 UTC
Permalink
Post by Alan Bellingham
[...]
Namespaces are hard and tricksy and nothing like as simple as naive
users think.
I don't know. I never write 'using namespace <whatever>'
anywhere in my code except in very rare circumstances
(that would be less than once every other year) into a
function's scope. That leaves only ADL to worry about.
For most of the last decade I have worked where this was
the official policy.
We never had any problem with namespaces and I find code
hard to read that uses them implicitly. (And the argument
that they are too much to type is, of course, BS. Code is
typed once, but read ten, hundred or thousend times. How
long it takes to type it is very irrelevant. What counts
is how easy it is to read and, even more important, how
easy it is to be mis-read.)
Post by Alan Bellingham
Alan Bellingham
Schobi
--
***@gmx.de is never read
I'm HSchober at gmx dot de
"I guess at some point idealism meets human nature and
explodes." Daniel Orner
Chris Uzdavinis (TeamB)
2008-07-30 16:53:47 UTC
Permalink
Post by Alan Bellingham
Post by Chris Uzdavinis (TeamB)
Hmm. The wording of the standard has changed significantly in this
section from the older copy I was reviewing. Alan, is that c++2003 or
from the new draft?
Ah, good point. That's ISO/IEC 14882:1998(E), which is what I also have
on hand.
My hardcopy (ISO/IEC 14882:2003) has unchanged wording.
Post by Chris Uzdavinis (TeamB)
anything though, as in just about every case, when I think Comeau has
an error it usually turns out I am the one who is mistaken. It
usually ends up being a learning experience.
Homer nods and all that, but Greg does tend to provide the gold
standard.
Ah, I see the difference. What you posted and what I had read were
different sections. (I wasn't paying close attention, actually, until
now.)

In 3.4.3.2:

Given X::m (where X is a user-declared namespace), [...], let S be
the set of all declarations of m in X and in the transitive closure
of all namespaces nominated by using-directives in X and its used
namespaces, EXCEPT THAT USING-DIRECTIVES ARE IGNORED IN ANY
NAMESPACE, INCLUDING X, DIRECTLY CONTAINING ONE OR MORE DECLARATIONS
OF m.

(emphasis added. Again citing 1998 standard which may have possibly
changed in the last ... decade.)

It is only after the set of names is created that overload resolution
occurs, and overload resolution is what your citation covered (in
7.3.4).

Ok, I'm fairly well convinced again that Comeau is right and Borland
is wrong in this case. It is not ambiguous, because the original code
was not doing unqualified name lookup, it was a qualified-id. Because
of that, what I cited above applies, and the using-directives are
ignored.
--
Chris (TeamB);
Fraser Ross
2008-07-30 17:58:16 UTC
Permalink
"Chris Uzdavinis (TeamB)"
Given X::m (where X is a user-declared namespace), [...], let S be
the set of all declarations of m in X and in the transitive closure
of all namespaces nominated by using-directives in X and its used
namespaces, EXCEPT THAT USING-DIRECTIVES ARE IGNORED IN ANY
NAMESPACE, INCLUDING X, DIRECTLY CONTAINING ONE OR MORE DECLARATIONS
OF m.
Thats changed to:
except that using-directives that nominate non-inline namespaces (7.3.1)
are ignored in any namespace,
including X, directly containing one or more declarations of m.

The example following compiles. Are inline namespaces new?

Fraser.
Fraser Ross
2008-07-30 18:38:58 UTC
Permalink
Another related paragraph is 3.3.8/4:

During the lookup of a name qualified by a namespace name, declarations
that would otherwise be made visible by
a using-directive can be hidden by declarations with the same name in
the namespace containing the using-directive;
see (3.4.3.2).

Fraser.
Fraser Ross
2008-07-30 15:28:06 UTC
Permalink
"Alan Bellingham"
Thats paragraph 7 in the latest draft. Its still the same. The example
in paragraph 4 which starts with the words 'The using-directive is
transitive' shows 3 examples of name hiding. I still think my program
demonstrates hiding. Paragraph 3 is the most relevant.

Fraser.
Fraser Ross
2008-08-02 18:40:53 UTC
Permalink
Post by Fraser Ross
I discovered a bug which is reported as 18810. It is present with a
later build although the report says its fixed. It is produced with the
namespace A {}
class B{
using namespace A;
};
Is this bug present in the most recent compiler?

Fraser.

Loading...