#include <iostream>
using namespace std;
void f(int){cout << "gi";}
template<class t> void tmpl(){
f('a');
f(t());
}
void f(char){cout << "gc";}
template<class t> void another_tmpl(){
f('a');
f(t());
tmpl<t>(); // Line 1
} // POI at Line 2
int main(){
another_tmpl<char>();
cout << endl;
tmpl<char>();
}
I expect the output of this code after two phase lookup to be
gcgcgcgc
gcgc // due to ODR
This is because of POI for tmpl<T>() in Line 1 is at Line 2 and at
this point 'f(char)' overload is visible.
$14.6.4.1/1 states - "For a function template specialization, a member
function template specialization, or a specialization for a member
function or static data member of a class template, if the
specialization is implicitly instantiated because it is referenced
from within another template specialization and the context from which
it is referenced depends on a template parameter, the point of
instantiation of the specialization is the point of instantiation of
the enclosing specialization. Otherwise, the point of instantiation
for such a specialization immediately follows the namespace scope
declaration or definition that refers to the specialization."
But gcc gives the output
gcgcgigi
gigi
What is it that I am missing here?
Regards,
Dabs.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
I see no specializations going on. I see instantiations, thus the
portion of the standard you quoted seems to have no impact here. What
you are doing is instantiating several templates. I would look at C+
+03 14.6 Name Resolution, specifically 14.6.3 Non-dependent names. Any
behavior except what gcc did would surprise me. When you instantiate
tmpl, lookup for stuff used in tmpl is done from the point of view of
the use, what's in scope at tmpl. At tmpl, only "void f(int)" is in
scope, so it uses that one. For another_tmpl, "void f(int)" and "void f
(char)" are both in scope, so it chooses the better fit one.
> Hi,
>
> #include <iostream>
> using namespace std;
>
> void f(int){cout << "gi";}
>
> template<class t> void tmpl(){
> f('a');
This expression does not depend on any template parameters, so only
functions f that are visible here will be considered.
> f(t());
This expression depends on the template parameter, so you get two-phase
lookup. The overload-set that will be considered for this call consists
of
- functions f that are found with normal lookup from here
- functions f that are found using ADL from here.
- functions f that are found using ADL from the POI.
> }
>
> void f(char){cout << "gc";}
This function will never be considered for the calls in tmpl<>.
Normal lookup won't find it, because it is declared after tmpl, and ADL
won't find it because it is not in an associated namespace of char
(char does not have any associated namespaces).
>
> template<class t> void another_tmpl(){
> f('a');
> f(t());
> tmpl<t>(); // Line 1
> } // POI at Line 2
>
> int main(){
> another_tmpl<char>();
> cout << endl;
> tmpl<char>();
> }
>
> I expect the output of this code after two phase lookup to be
>
> gcgcgcgc
> gcgc // due to ODR
>
> This is because of POI for tmpl<T>() in Line 1 is at Line 2 and at
> this point 'f(char)' overload is visible.
>
> $14.6.4.1/1 states - <snip>
>
> But gcc gives the output
>
> gcgcgigi
> gigi
>
> What is it that I am missing here?
You are missing the intricasies of name lookup in a template. See clause
14.6, in particular 14.6/8, 14.6/9 and 14.6.4.2.
>
> Regards,
> Dabs.
>
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
What you wrote was not any of these.
It's simply function template declarations, not function template
specializations.
I think you are wrong.
another_tmpl<char>();
tmpl<char>();
Both of them name and call specializations of function templates.
Strange, on i686-apple-darwin9-g++-4.0.1 (GCC) 4.0.1 (Apple Inc. build
5465)
i get:
gcgcgigc
gigc
{ edits: quoted banner removed. don't quote extraneous material. -mod }
I tried Visual Studio 2008. It delivers:
gcgcgcgc
gcgc
Those are calls to functions which are instantiations of the general
template function. They do not call specializations, since no
specialization of that template function were made.
A specialization would look like
template<> void tmpl<char>()
{
/* something */
What's up with the example in 14.6/9 then?
void f(char);
template<class T> void g(T t)
{
f(1);
f(T(1));
f(t);
}
void f(int);
void h()
{
g(2);
g('a');
}
It says that g(2) will yield to two calls of f(int) and and g('a') of
three calls of f(char). None of those have ADL involved but rather
normal unqualified lookup, which would imply f(int) would never be
chosen. Actually, although not normative, that example confused the
hell out of me already in the past. Instinctively, i would have
expected this output
gcgcgigc
gigc
Which is what apple's gcc seem to print. But it's not really what
14.6.4.2 says, as you quote it too. The example seems to contradict
it. To solve this, i just assumed that "template definition context"
actually doesn't consist of just all the names visible at the point of
definition of the template, but rather the whole declarative region in
which the template appears in - which would make the example valid
again and would find f(int) aswell as f(char) in Dabs' example because
both appear in the global namespace.
I'll link you to the thoughts i had back then:
http://stackoverflow.com/questions/403929/which-compiler-is-correct-for-the-
following-overloading-specialization-behavior/404000#404000
. Can anyone clear this issues up once and for all please? :)
--
No. They instantiate function templates and then call
them. That's different from specializing them.
Schobi
That's right. I was confused by the example in 14.6/9 which
illustrates dependent name lookup contradictory to the rules of
14.6.4.2 (which says only ADL is effective at the POI). Nevermind my
post asking about that example before. I've found a defect report
about that example here:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#197
. Posting it because it may be helpful to others that are likewise
confused by it. I hope it will be fixed for C++1x :)
--
14.8/1 :
"A function instantiated from a function template is called a function
template specialization; so is an
explicit specialization of a function template. Template arguments can
either be explicitly specified when
naming the function template specialization or be deduced (14.8.2)
from the context, e.g. from the function
arguments in a call to the function template specialization."
What you have written is an explicit specialization. Also read 14.2,
which defines the syntax as how you may refer to a specialization
which is by a template-id (name<parameters>).
> What's up with the example in 14.6/9 then?
>
> void f(char);
> template<class T> void g(T t)
> {
> f(1);
> f(T(1));
> f(t);
> }
>
> void f(int);
>
> void h()
> {
> g(2);
> g('a');
> }
>
This example has been fixed in the draft (n2798.pdf) where void f(int)
has been changed
to void f(E e); where E is an enumeration and does have an associated
namespace. Otherwise your confusion would be justified. And remember
the examples are non-normative - so where the normative text is
contradicted by the example, the normative text wins.
> . Can anyone clear this issues up once and for all please? :)
Hopefully it is clear now. Vandevoorde's book does a really good job
of describing two-phase lookup if i remember correctly.
The relevant sections in the standard (n2798) are: 3.4.1, 3.4.2,
14.6.2/1, 14.6.4.
And they should state the intent of the following unambiguously (the
spirit of which is captured succinctly by Ingen Schenau's explanation
above) - and i am focusing only on function calls - and once again
this represents how i think of this process (and i speak with little
to no authority here - i.e. this is my interpretation of the
standard):
- dependent names are not resolved until the template is
instantiated (i.e. Point of Instantiation, POI)
- two sets of function-declarations (i.e. that declare functions
with our name-of-interest, NOI), are created and Overload Resolution
then operates within the union of both sets
Set 1, defintion-context-set, DCS
-------------------------------------
- when the definition of the template is encountered, the name-of-
interest (NOI) is looked up using "name lookup" (which includes 3.4.1
(non-ADL), 3.4.2 (ADL) if unqualified, or 3.4.3 if qualified) - and
all visible function-declarations (that declare our NOI) are stored in
the definition-context-set (this set is then not altered).
Set 2, instantiation-context-set, ICS
--------------------------------------
- at the point of instantiation of the template the following has to
occur:
- the set of namespaces (SAN) associated with the types of all the
arguments is formed
- if SAN is empty - ICS is empty
- else all function-declarations that declare the name-of-interest
within any of those namespaces are added to the ICS
A union of all the function declarations in the ICS and the DCS is
then formed, and overload resolution is used to identify the best
function to call.
So for example, considering only unqualified dependent function calls
in templates:
//-----------------------------------------
void f(int); //-->1
template <class T> void f(T); //-->2
template<class T> int g(T t)
{
f(t);
}
void f(char); //-->3
g('b'); // calls 2
//-----------------------------------------
Now get ADL into it:
struct S { };
void f(int); //-->1
template <class T> void f(T, S* = 0); //-->2
template<class T> int g(T t)
{
f(t,(S*)0);
}
void f(char,S*); //-->3
g('b'); // calls 3
//---------------------------------
But don't forget that it is the *type* of the argument at call that
governs associated NS
struct S { };
void f(int); //-->1
template <class T> void f(T, S* = 0); //-->2
template<class T> int g(T t)
{
f(t, 0);
}
void f(char, S* = 0); //-->3
g('b'); // calls 2 again
//----------------------------------
Now as regarding qualified NL, i feel the standard could be clearer.
I think the intent of the standard is to say that qualified NL of
dependent names only occurs at POD. But 14.6.4 could be interpreted to
suggest that qualified name lookup actually considers any new
declarations added to the enclosing namepsace since POD, if any of the
arguments of the function call have an associated namespace that is
the same as the namespace the function is a member of.
Remember, it does not explicitly say that namelookup occurs via 3.4.2
- just that only associated namespaces are searched at POI. Anyway, I
feel it could be a little clearer.
struct S { };
void f(int); //-->1
template <class T> void f(T, S*=0); //-->2
template<class T> int g(T t)
{
::f(t, (S*)0);
}
void f(char, S* = 0); //-->3
g('b'); // should this call 2 or 3 ?
// I think the standard intends this to call 2
Anyway, hope that helps.
regards,
Faisal Vali
Radiation Oncology
Loyola
That will call 2 - because ::f is a qualified-id, not an identifier,
and so it is not a dependent name according to 14.6.2/1 . It then is
not looked up deferred, but by the rules of 14.6.3/1.
> Anyway, hope that helps.
>
Indeed, I understand the stuffs now. Thanks for your answer, has
really helped me. I've found another interesting thing. The following
looks like ill-formed, but it is undefined behavior according to
14.6.4.2:
// -- snip --
struct S { };
template<typename T>
void f() {
g((T*)0);
}
void h() {
f<S>();
}
int main() {
h();
// works on both comeau and GCC. but beware, it's UB!
f<S>();
}
void g(S*) { }
// -- snap --
While I agree that the standard intends it to call 2, I feel one could
interpret the standard such that lookup in the instantiation-context
is triggered here (in the qualified NS) by adding that NS also as an
associated NS. Nothing in 3.4.2 or clause 14 clearly forbids this. I
feel it could benefit from clearer language.
Also per 14.6.2.2/3, an *id-expression* (::f) is dependent if it
contains an *identifier* (t) that was declared w a dependent type.
And per 5.1/1 an id-expression includes a qualified-id.
> Indeed, I understand the stuffs now. Thanks for your answer, has
> really helped me. I've found another interesting thing. The following
> looks like ill-formed, but it is undefined behavior according to
> 14.6.4.2:
>
> // -- snip --
> struct S { };
>
> template<typename T>
> void f() {
> g((T*)0);
>
> }
>
> void h() {
> f<S>();
>
> }
>
> int main() {
> h();
> // works on both comeau and GCC. but beware, it's UB!
> f<S>();
>
> }
>
> void g(S*) { }
> // -- snap --
I would agree with you here.
thanks,
Faisal Vali
Radiation Oncology
Loyola
--