Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Forwarding Function Template Resolution and Explicit Template Argument

9 views
Skip to first unread message

MB

unread,
Nov 30, 2005, 5:54:52 AM11/30/05
to
Hi, gurus.

GCC3.4.4 and Comeau cannot resolve
'overloaded forwarding function templates'
that require 'explicit template arguments'.
See:

template< class Ex, class T >
void f1(T& x) { }

template< class Ex, class T >
void f1(const T& x) { }

void test_f1()
{
const char c = 1; // if const,
f1<int>(c); // makes ambiguity-error!
}

Is that conforming? Why...?


Regards,
MB
http://p-stade.sourceforge.net/


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Gene Bushuyev

unread,
Nov 30, 2005, 10:08:26 PM11/30/05
to
"MB" <mb2...@yahoo.co.jp> wrote in message
news:1133294886.2...@o13g2000cwo.googlegroups.com...

> Hi, gurus.
>
> GCC3.4.4 and Comeau cannot resolve
> 'overloaded forwarding function templates'
> that require 'explicit template arguments'.
> See:
>
> template< class Ex, class T >
> void f1(T& x) { }
>
> template< class Ex, class T >
> void f1(const T& x) { }
>
> void test_f1()
> {
> const char c = 1; // if const,
> f1<int>(c); // makes ambiguity-error!
> }

<int> is a red herring, so let's just remove the unused template arguments:

template<class T >
void f1(T& x) { }

template<class T >
void f1(const T& x) { }

void test_f1()
{
const char c = 1;

f1(c);
}

Now you can see that both f1(...) overloads match with T = const char: both take
a reference to const char. The second function has an extra const qualifier
which is ignored.


-- Gene Bushuyev
----------------------------------------------------------------
There is no greatness where there is no simplicity, goodness and truth. ~ Leo
Tolstoy

Maxim Yegorushkin

unread,
Dec 1, 2005, 10:47:54 AM12/1/05
to

Gene Bushuyev wrote:

[]

> <int> is a red herring, so let's just remove the unused template arguments:
>
> template<class T >
> void f1(T& x) { }
>
> template<class T >
> void f1(const T& x) { }
>
> void test_f1()
> {
> const char c = 1;
> f1(c);
> }
>
> Now you can see that both f1(...) overloads match with T = const char: both take
> a reference to const char. The second function has an extra const qualifier
> which is ignored.

This looks wrong, since at least comeau online and gcc 3.4.4 disagree
with you.

Carl Barron

unread,
Dec 2, 2005, 6:45:03 AM12/2/05
to
In article <y_ojf.21196$D13....@newssvr11.news.prodigy.com>, Gene

Bushuyev <sp...@spamguard.com> wrote:
>
> <int> is a red herring, so let's just remove the unused template
> arguments:
>
is it a red herring, see below.

> template<class T >
> void f1(T& x) { }
>
> template<class T >
> void f1(const T& x) { }
>
> void test_f1()
> {
> const char c = 1;
> f1(c);
> }
>
> Now you can see that both f1(...) overloads match with T = const char:
> both take
> a reference to const char. The second function has an extra const
> qualifier
> which is ignored.
> \ Bushuyev

consider
template < class T >
void f1(T& ) { }

template<class T >
void f1(const T&) { }


template <class T,class U>
void f2(U &) {}

template <class T,class U>
void f2(const U &) {}


void test_f1()
{
const char c = 1; // if const,

f1(c); // makes ambiguity-error! n
f2<int>(c);
}

CodeWarrior 9 produces this error:
Error : ambiguous access to overloaded function
'f2<int, const char>(const char &)'
'f2<int, char>(const char &)'
HelloWorld.cp line 19 f2<int>(c); // produces ambiguity error

if line 19 is commented out there is no error. so the int
parameter is not a red herring.

The why is buried in the legalize of section 14 of the 1998 standard.
and the jan 2005 draft.

kanze

unread,
Dec 2, 2005, 6:54:14 AM12/2/05
to
Maxim Yegorushkin wrote:
> Gene Bushuyev wrote:

> []

> > <int> is a red herring, so let's just remove the unused
> > template arguments:

> > template<class T >
> > void f1(T& x) { }

> > template<class T >
> > void f1(const T& x) { }

> > void test_f1()
> > {
> > const char c = 1;
> > f1(c);
> > }

> > Now you can see that both f1(...) overloads match with T =
> > const char: both take a reference to const char. The second
> > function has an extra const qualifier which is ignored.

> This looks wrong, since at least comeau online and gcc 3.4.4
> disagree with you.

I'm going to try to do this without spending hours looking
everything up in the standard, so I'll probably get some details
wrong, but I think what happens is that the compiler
instantations both templates (at least the declarations), the
first with T == char const, and the second with T == char, and
that overload resolution selects the the second, because it is
"more specialized".

The critical points are that argument deduction is applied to
each of the template function declarations separately (and works
for both), that only the declarations are instantiated for
overload resolution (irrelevant here, but important if the non
const version tries to modify something), and that the degree of
template specialization is a tie breaker in cases of overload
resolution. (To be truthful, I'm not sure at what point it
kicks in, but since we have two exact matches here, all that
matters is that it does kick in at some point.)

With regards to Gene's point, top level const is ignored, but
the top level, here, is reference, and references are always
const. What the reference refers to is not top level, so its
const-ness is considered. I suspect that he's getting confused
by the fact that both templates *are* instantiated, and that the
function signature of both, after instantiation, is exactly
the same.

I might add that there's no shame in being confused by this.
I'm only beginning to understand the issues myself, largely as a
result of a recent thread in fr.comp.lang.c++, where Franck
Branjonneau pointed them out to me.

--
James Kanze GABI Software
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

Gene Bushuyev

unread,
Dec 3, 2005, 6:28:51 AM12/3/05
to
"Carl Barron" <cbarr...@adelphia.net> wrote in message
news:011220052217289911%cbarr...@adelphia.net...

> In article <y_ojf.21196$D13....@newssvr11.news.prodigy.com>, Gene
> Bushuyev <sp...@spamguard.com> wrote:
[...]

> CodeWarrior 9 produces this error:
> Error : ambiguous access to overloaded function
> 'f2<int, const char>(const char &)'
> 'f2<int, char>(const char &)'
> HelloWorld.cp line 19 f2<int>(c); // produces ambiguity error
>
> if line 19 is commented out there is no error. so the int
> parameter is not a red herring.
>
> The why is buried in the legalize of section 14 of the 1998 standard.
> and the jan 2005 draft.

I wish you provided a reference to the passage. Chapter 14 is 60 pages long.
As far as I remember there is no difference in the standard rules of
deducing the arguments for f1(c) or f2<int>(c). So either both should
compile or both should fail.
As James Kanze suggested the second version (of each function) is more
specialized than the first, so it should be chosen. That may be the case as
some good compilers chose the const version. I will check with the standard
when I get time for it. My reasoning was that with deduced template argument
"const char" the first template would be f1(char const &) and the second -
f1(char const const &), which is exactly the same.

Regarding the compilers behavior, they are all different. Here is the code I
tested:
//----------------------------
// [12/2/2005]
//----------------------------
#include <iostream>
#include <ostream>

template < class T >
void f1(T& ) { std::cout << "\nf1(T&)"; }

template<class T >
void f1(const T&) { std::cout << "\nf1(const T&)"; }


template <class T,class U>
void f2(U &) { std::cout << "\nf2(U&)"; }

template <class T,class U>
void f2(const U &) { std::cout << "\nf2(const U&)"; }

int main(int argc, char* argv[])


{
const char c = 1;

f1(c); // #1
f2<int>(c); // #2

return 0;
}

I checked VC7.1, VC8, gcc 3.2.3, and VC and EDG from
http://www.dinkumware.com/exam/dinkumExam.aspx and here is what they said

compiler line #1 line #2
VC7.1 (8) ok ok
gcc3.2.3 ok ok
VC(dinkum) fail fail
EDG(dinkum) ok fail
CW9 ok fail

all failures are due to reported ambiguities.

-- Gene Bushuyev ~ Cadence Design Systems

kanze

unread,
Dec 5, 2005, 5:36:56 AM12/5/05
to
Gene Bushuyev wrote:
> "Carl Barron" <cbarr...@adelphia.net> wrote in message
> news:011220052217289911%cbarr...@adelphia.net...
> > In article
> > <y_ojf.21196$D13....@newssvr11.news.prodigy.com>, Gene
> > Bushuyev <sp...@spamguard.com> wrote:

> [...]
> > CodeWarrior 9 produces this error:
> > Error : ambiguous access to overloaded function
> > 'f2<int, const char>(const char &)'
> > 'f2<int, char>(const char &)'
> > HelloWorld.cp line 19 f2<int>(c); // produces ambiguity error

> > if line 19 is commented out there is no error. so the int
> > parameter is not a red herring.

> > The why is buried in the legalize of section 14 of the 1998
> > standard. and the jan 2005 draft.

> I wish you provided a reference to the passage. Chapter 14 is
> 60 pages long. As far as I remember there is no difference in
> the standard rules of deducing the arguments for f1(c) or
> f2<int>(c). So either both should compile or both should
> fail.

Yah, but you generally have to read it all in excrutiating
detail to understand anything. Supposing that you manage to
understand anything even then.

Like you, I expect both cases to behave identically.

> As James Kanze suggested the second version (of each function)
> is more specialized than the first, so it should be chosen.

There is definitly a clause concerning this, see §13.3.3/1,
third point in the second list. I sort of think it applies
here, but I wouldn't swear to it.

> That may be the case as some good compilers chose the const
> version. I will check with the standard when I get time for
> it. My reasoning was that with deduced template argument
> "const char" the first template would be f1(char const &) and
> the second - f1(char const const &), which is exactly the
> same.

I'm pretty sure that in the first case (f1(T&)), argument
deduction results in T == char const, and in the second (f1(T
const&)), argument deduction results in T == char. So both
instantiations have exactly the same signature. They are *not*
the same function, however, and by a special rule (§14.5.5.1/3),
do not have the same signature. (My impression is that the
definition of "signature" has been somewhat bent here. But it
is clear that if the rules in §13.3.3/1 mentionned above is to
by applied, information concerning the templat having been
specialized must be carried on into the function overload
phase.)

> Regarding the compilers behavior, they are all different. Here
> is the code I tested:

What is curious is the number of compilers which do fail for
f2<int>, but not for f1? You have to expect a certain number of
random bugs with any compiler, but when so many distinct
developments have the same "bug", you have to wonder a little
bit.

> return 0;
> }

I've got three versions of g++ here, both 3.3.3 and 3.4.3 fail
on line #w, but 4.0.2 accepts both lines again. Normally, I'd
just chalk it up to a regression introduced into the 3.3 tree,
and corrected in 4.0. But a regression which just happens to
correspond exactly to the behavior of EDG and CW9 (and Sun CC
5.5, while I'm at it)? For the moment, I can't find anything in
the standard that would justify the difference, but I can't
understand why so many implementations would have exactly the
same error, either.

Maybe someone responsible for the implementations will step up
and explain it to us.

--
James Kanze GABI Software
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

MB

unread,
Dec 6, 2005, 5:49:18 AM12/6/05
to
Thank you for all the responses.
When I had to pass only types to functions, I ran afoul of it.
It is surprising that this is a subtle problem.

My workaround for GCC is:

#include <boost/type_traits/is_const.hpp>
#include <boost/utility/enable_if.hpp>

template< class T, class R >
struct disable_if_const : boost::disable_if< boost::is_const<T>, R > {
};

template< class Ex, class T >

typename disable_if_const<T, void>::type f1(T& x) { }

template< class Ex, class T >
void f1(const T& x) { }

void test_f1()
{


const char c = 1;

f1<int>(c); // ok!
}

But I think something like type2type is a better choice for more
general cases.

Regards,
MB
http://p-stade.sourceforge.net/

0 new messages