Topic: Overload resolution from within template functions
Author: jth02@arcor.de (Jens Theisen)
Date: Mon, 8 Jan 2007 23:54:18 GMT Raw View
Hello,
I came accross the following behaviour of gcc 4.1, to which comeau's
tryitout agrees, so it's probably correct.
struct X { };
struct Y { };
namespace NS {
template<class T>
void f(const T*)
{
typedef typename T::fish soup;
}
void f(const X*) { }
template<class T>
void g()
{
f((const T *) 0);
}
void f(const Y*) { }
void testEnumAsParam()
{
g< X >();
g< Y >();
}
}
fails to compile on g< Y >(), because the "wrong" overload is
taken. But why?
It works when
- the overload for Y is is put before it's call in the template or
- the overload can be found by Koenig lookup or
- the overload is turned into an explicit specialisation
Is the language really requiring this? If so, why?
And why does the last two of the three points above make a difference?
Thanks for any enlightening!
--
Cheers, Jens
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Tue, 9 Jan 2007 13:48:05 CST Raw View
On Jan 8, 3:54 pm, j...@arcor.de (Jens Theisen) wrote:
> I came accross the following behaviour of gcc 4.1, to which comeau's
> tryitout agrees, so it's probably correct.
>
> struct X { };
> struct Y { };
>
> namespace NS {
>
> template<class T>
> void f(const T*)
> {
> typedef typename T::fish soup;
>
> }void f(const X*) { }
>
> template<class T>
> void g()
> {
> f((const T *) 0);
>
> }void f(const Y*) { }
>
> void testEnumAsParam()
> {
> g< X >();
> g< Y >();
>
> }
> }fails to compile on g< Y >(), because the "wrong" overload is
> taken. But why?
Your program is not accounting for the point of instantiation of the
g() function template. (See 14.6.4.1). At g()'s point of
instantiation, the f(const Y*) overload is not visible (it first
appears in the translation unit just after g()'s declaration). Moving
the function, explicitly instantiating the template and other factors
can influence the point of instantiation. (Note also that gcc versions
prior to version 4.1 did not always instantiate templates at the
correct point, so programs such as this one would be accepted).
In practical terms, the assumption is that whover wrote the function
template g() and put in the call to f() had no reason to know that
another f() would be declared later on (in fact the compiler did not
that another f() existed when it first reached g()'s declaration.)
Therefore, it makes sense to select from the f() overloads visible at
g()'s point of instantiation to match the call to f(), lest the
programmer be surprised by calling an f() overload completely unknown
to g's author.
The solution (and always a good practice) is to declare all functions
before their implementations. Externally visible functions should be
declared in a header file, and file scope functions should be declared
at the top of the source file. This convention helps document the
contents of a source file contains, and ensures that all functions are
visible at any point within the translation unit.
The Standard also offers this, more sobering advice:
"When writing a specialization, be careful about its location; or to
make it compile will be such a trial as to kindle its self-immolation.
[14.7.3/]"
Aside from the obvious warning this paragraph makes about the dangers
of drinking and writing at the same time, I can also personally attest
to the vast numbers of computers that my misplaced template
declarations have reduced to smoldering piles of fine ash.
Greg
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]