473,396 Members | 1,942 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Overloading template functions

Hi!

Here is a problem I ran into at work. The following example doesn't
compile on gcc-4.1:

struct cons_end {};

template<typename U,typename Vstruct cons {
U elem;
V tail;
};

template<typename U, typename V>
void foo4(U elem, V tail)
{
::foo4(tail.elem,tail.tail);
}

template<typename U>
void foo4(U elem, cons_end tail)
{
}

int main()
{
typedef cons<int,cons<int,cons_end tList;
tList list;
foo4(list.elem,list.tail);
}
The problem occurs in the first call to foo4 [U = int, V = cons<int,
cons_end>]. Now the compiler needs to resolve the nested call to (the
overloaded) foo4 given parameters of type int and cons_end. However,
not the template<typename Ufoo4(U, cons_end) is selected, but again
the generic version, leading to the following error:

test1.cpp: In function 'void foo4(U, V) [with U = int, V = cons_end]':
test1.cpp:22: instantiated from 'void foo4(U, V) [with U = int, V =
cons<int, cons_end>]'
test1.cpp:58: instantiated from here
test1.cpp:22: error: 'struct cons_end' has no member named 'elem'
test1.cpp:22: error: 'struct cons_end' has no member named 'tail'

If I change the order of the definitions like so...

struct cons_end {};

template<typename U,typename Vstruct cons {
U elem;
V tail;
};

/* N.B. THE TWO VERSIONS OF FOO4() NOW IN DIFFERENT ORDER */
template<typename U>
void foo4(U elem, cons_end tail)
{
}

template<typename U, typename V>
void foo4(U elem, V tail)
{
::foo4(tail.elem,tail.tail);
}

int main()
{
typedef cons<int,cons<int,cons_end tList;
tList list;
foo4(list.elem,list.tail);
}

.... the code compiles, the cons_end version of foo4 is selected.

So my question now is: Is this correct behaviour or not? What does the
standard say? If the first version is wrong, why exactly?

Thanks,

Mattias

May 16 '07 #1
8 1930
ma*************@googlemail.com wrote:
Hi!

Here is a problem I ran into at work. The following example doesn't
compile on gcc-4.1:

struct cons_end {};

template<typename U,typename Vstruct cons {
U elem;
V tail;
};
at least, declare the function that you want to call before calling it:

template<typename U>
void foo4(U elem, cons_end tail);
template<typename U, typename V>
void foo4(U elem, V tail)
{
::foo4(tail.elem,tail.tail);
}
you want the one argument template function to be called, but you
haven't declared it. At this point, ::foo4 refers to the only declared
(and defined) one, that is that one with two templates argument.
>
template<typename U>
void foo4(U elem, cons_end tail)
{
}

int main()
{
typedef cons<int,cons<int,cons_end tList;
tList list;
foo4(list.elem,list.tail);
return 0;
}

If I change the order of the definitions like so...
... the code compiles, the cons_end version of foo4 is selected.
because now the overloaded function is defined at the point in which is
called.

Regards,

Zeppe
May 16 '07 #2

<ma*************@googlemail.comwrote in message
news:11*********************@u30g2000hsc.googlegro ups.com...
Hi!

Here is a problem I ran into at work. The following example doesn't
compile on gcc-4.1:

struct cons_end {};

template<typename U,typename Vstruct cons {
U elem;
V tail;
};

template<typename U, typename V>
void foo4(U elem, V tail)
{
::foo4(tail.elem,tail.tail);
}

template<typename U>
void foo4(U elem, cons_end tail)
{
}

int main()
{
typedef cons<int,cons<int,cons_end tList;
tList list;
foo4(list.elem,list.tail);
}
The problem occurs in the first call to foo4 [U = int, V = cons<int,
cons_end>]. Now the compiler needs to resolve the nested call to (the
overloaded) foo4 given parameters of type int and cons_end. However,
not the template<typename Ufoo4(U, cons_end) is selected, but again
the generic version, leading to the following error:

test1.cpp: In function 'void foo4(U, V) [with U = int, V = cons_end]':
test1.cpp:22: instantiated from 'void foo4(U, V) [with U = int, V =
cons<int, cons_end>]'
test1.cpp:58: instantiated from here
test1.cpp:22: error: 'struct cons_end' has no member named 'elem'
test1.cpp:22: error: 'struct cons_end' has no member named 'tail'

[.. snap ..]

So my question now is: Is this correct behaviour or not?
Yes
What does the standard say? If the first version is wrong, why exactly?
----------
14.6.2/1
In an expression of the form:
postfix-expression ( expression-listopt )
where the postfix-expression is an identifier, the identifier denotes a
dependent name if and only if any of the expressions in the expression-list
is a type-dependent expression (14.6.2.2).
----------

and

----------
14.6.3/1
Non-dependent names used in a template definition are found using the usual
name lookup and bound at the point they are used.
----------
The function call expression in foo4 is:
::foo4(tail.elem,tail.tail);

"::foo4" is the postfix-expression here, and it's not an identifier (but a
fully qualified name). So it's not a dependent name, therefore regular name
lookup is used as with normal functions, at the point of definition.
However, if you used
foo4(tail.elem, tail.tail)
(note the omission of "::"), foo4 would be a dependent name, in which case:

----------
14.6.4
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.
----------

tail.tail is of type ::cons_end, thus the global namespace is searched for
the name 'foo4' at point of instantiation (in main()). In main(),
foo4(cons<T,cons_end>) is known, and because it is more specialized, that
function is called.
- Sylvester
May 16 '07 #3
On May 16, 4:06 pm, "Sylvester Hesp" <s.h...@oisyn.nlwrote:
What does the standard say? If the first version is wrong, why exactly?

----------
14.6.2/1
In an expression of the form:
postfix-expression ( expression-listopt )
where the postfix-expression is an identifier, the identifier denotes a
dependent name if and only if any of the expressions in the expression-list
is a type-dependent expression (14.6.2.2).
----------

and

----------
14.6.3/1
Non-dependent names used in a template definition are found using the usual
name lookup and bound at the point they are used.
----------

The function call expression in foo4 is:
::foo4(tail.elem,tail.tail);

"::foo4" is the postfix-expression here, and it's not an identifier (but a
fully qualified name). So it's not a dependent name, therefore regular name
lookup is used as with normal functions, at the point of definition.
Thanks, that clarifies a lot!
However, if you used
foo4(tail.elem, tail.tail)
(note the omission of "::"), foo4 would be a dependent name, in which case:

----------
14.6.4
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.
----------

tail.tail is of type ::cons_end, thus the global namespace is searched for
the name 'foo4' at point of instantiation (in main()). In main(),
foo4(cons<T,cons_end>) is known, and because it is more specialized, that
function is called.
Yeah, gcc implements that behaviour, the whole thing working without
the "::" qualifier is why I originally asked here.

Now what if I need to call, say, a function in another namespace. I
can't just write the (qualified) name, since that would make it
syntactically something that is not an identifier, right? Is there
some 'good practice' way to handle this situation? [I'm just curious,
I probably have a correct workaround for my original problem now.]

Cheers,

Mattias
May 16 '07 #4
Sylvester Hesp wrote:
tail.tail is of type ::cons_end, thus the global namespace is searched for
the name 'foo4' at point of instantiation (in main()). In main(),
foo4(cons<T,cons_end>) is known, and because it is more specialized, that
function is called.
testspecialization.cpp: In function ‘void foo4(U, V) [with U = int, V =
cons_end]’:
testspecialization.cpp:14: instantiated from ‘void foo4(U, V) [with U
= int, V = cons<int, cons_end>]’
testspecialization.cpp:26: instantiated from here
testspecialization.cpp:14: error: ‘struct cons_end’ has no member named
‘elem’
testspecialization.cpp:14: error: ‘struct cons_end’ has no member named
‘tail’

for g++ the istantiation of the second call to foo4 is inside foo4<U,V>.
I don't know if it is standard, but I suppose it is. A simple function
declaration works just well, and it is also quite reasonable, given that
you don't usually call a function without having declared it...

Regards,

Zeppe
May 16 '07 #5

"Zeppe" <zeppe@.remove.all.this.long.comment.email.itwro te in message
news:f2**********@aioe.org...
Sylvester Hesp wrote:
>tail.tail is of type ::cons_end, thus the global namespace is searched
for the name 'foo4' at point of instantiation (in main()). In main(),
foo4(cons<T,cons_end>) is known, and because it is more specialized, that
function is called.

testspecialization.cpp: In function ‘void foo4(U, V) [with U = int, V =
cons_end]’:
testspecialization.cpp:14: instantiated from ‘void foo4(U, V) [with U =
int, V = cons<int, cons_end>]’
testspecialization.cpp:26: instantiated from here
testspecialization.cpp:14: error: ‘struct cons_end’ has no member named
‘elem’
testspecialization.cpp:14: error: ‘struct cons_end’ has no member named
‘tail’

for g++ the istantiation of the second call to foo4 is inside foo4<U,V>.
-----------
14.6.4.1/1 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.
-----------

So, the point of instantiation is in main()
I don't know if it is standard, but I suppose it is. A simple function
declaration works just well, and it is also quite reasonable, given that
you don't usually call a function without having declared it...
Actually, it happens all the time:

#include <set>

class MyType
{
// whatever
};

bool operator <(MyType&, MyType&);

int main()
{
std::set<MyTypes;
s.insert(MyType);
}

std::set<Tuses std::less<Tto compare types. The (least specialized
version of) std::less<Tuses the < operator for comparison. But,
operator<(MyType&, MyType&) is declared *after* the implementation of
std::less<T(which is defined by including <setat the top of the
sourcefile).

Not having this kind of two-phase name lookup (14.6.4) is devestating for
the use of templates and generic programming. Luckily, compilers that do not
fully implement the two-phase name lookup (VC++ for example) do implement
the second part (lookup at point of instantiation), which is imho the most
important one.

- Sylvester
May 16 '07 #6
Sylvester Hesp wrote:
"Zeppe" <zeppe@.remove.all.this.long.comment.email.itwro te in message
news:f2**********@aioe.org...
>I don't know if it is standard, but I suppose it is. A simple function
declaration works just well, and it is also quite reasonable, given that
you don't usually call a function without having declared it...

Actually, it happens all the time:
true, and actually the code wasn't compiling due to an error introduced
by me :P.

Regards,

Zeppe
May 16 '07 #7
On May 16, 3:11 pm, Zeppe
<zeppe@.remove.all.this.long.comment.email.itwrote :
mattias.niss...@googlemail.com wrote:
Here is a problem I ran into at work. The following example doesn't
compile on gcc-4.1:
struct cons_end {};
template<typename U,typename Vstruct cons {
U elem;
V tail;
};
at least, declare the function that you want to call before calling it:
template<typename U>
void foo4(U elem, cons_end tail);
IMHO, it is generally a good idea to declare functions before
use. For the reader, even in cases where the compiler doesn't
need it.
template<typename U, typename V>
void foo4(U elem, V tail)
{
::foo4(tail.elem,tail.tail);
}
you want the one argument template function to be called, but you
haven't declared it. At this point, ::foo4 refers to the only declared
(and defined) one, that is that one with two templates argument.
I'm not sure; this is one of the more complicated aspects of
C++. But as I understand it, the problem is that the :: in
front of foo prevent the lookup from being dependent, so name
binding occurs at the point of definition. Since in this case,
the author probably wants dependent lookup (which takes place at
the point of instantiation), he should in any case drop the ::,
which are typically used to tell the compiler *not* to consider
anything not yet visible.

(In this particular case, I still think it would be clearer to
declare the function before use. But I'd also drop the ::,
because I think dependant name lookup for foo4 is really what is
wanted.)
template<typename U>
void foo4(U elem, cons_end tail)
{
}
int main()
{
typedef cons<int,cons<int,cons_end tList;
tList list;
foo4(list.elem,list.tail);
return 0;
}
If I change the order of the definitions like so...
... the code compiles, the cons_end version of foo4 is selected.
because now the overloaded function is defined at the point in which is
called.
And because that definition is also a declaration---all that is
necessary, of course, is that a declaration be visible.

It's important to realize, though, that even though the function
is a template, name lookup does NOT depend on the instantiation
types, and occurs at the point of definition of the template, if
the name isn't dependent, and that using :: is one way to ensure
that the name isn't dependent.

--
James Kanze (Gabi Software) email: ja*********@gmail.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

May 16 '07 #8
James Kanze wrote:
It's important to realize, though, that even though the function
is a template, name lookup does NOT depend on the instantiation
types, and occurs at the point of definition of the template, if
the name isn't dependent, and that using :: is one way to ensure
that the name isn't dependent.
using (foo)(params) is another, with a slightly different semantic
meaning (names are looked up in the currently visible scopes, rather
than in the specified namespace). That may be preferable because it
does not force you to specify the complete namespace scope, thus
improving maintainability.

- Sylvester
May 16 '07 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

17
by: Terje Slettebø | last post by:
To round off my trilogy of "why"'s about PHP... :) If this subject have been discussed before, I'd appreciate a pointer to it. I again haven't found it in a search of the PHP groups. The PHP...
4
by: Dave Theese | last post by:
Hello all, I'm trying to get a grasp of the difference between specializing a function template and overloading it. The example below has a primary template, a specialization and an overload. ...
5
by: Bolin | last post by:
I am frustrated as I am trying to design a toy image library. I want the user to write mul(im, 2) if s/he wants to multiply the image by 2, and mul(im1, im2) if s/he wants to multiply...
11
by: gao_bolin | last post by:
I am facing the following scenario: I have a class 'A', that implements some concept C -- but we know this, not because A inherits from a virtual class 'C', but only because a trait tell us so: ...
16
by: WittyGuy | last post by:
Hi, What is the major difference between function overloading and function templates? Thanks! http://www.gotw.ca/resources/clcm.htm for info about ]
9
by: Ann Huxtable | last post by:
I have the following code segment - which compiles fine. I'm just worried I may get run time probs - because it looks like the functions are being overloaded by the return types?. Is this Ok: ? ...
6
by: flopbucket | last post by:
Could someone explain to me what the difference is between function template specialization and function overloading? I guess overloading can change the number of parameters, but otherwise they...
11
by: jakester | last post by:
I am using Visual C++ 2007 to build the code below. I keep getting linkage error. Could someone please tell me what I am doing wrong? The code works until I start using namespace for my objects. ...
3
by: Adam Nielsen | last post by:
Hi everyone, I've run into yet another quirk with templates, which IMHO is a somewhat limiting feature of the language. It seems that if you inherit multiple classes, and those classes have...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.