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

Koenig lookup hides ordinary function

33 views
Skip to first unread message

Khoguan Phuann

unread,
Jul 28, 2005, 9:15:27 PM7/28/05
to
I have been searching for Koenig lookup examples on the Net.
Though many of them were found, none of them seems to address
the following situation:

#include <vector>

int distance(std::vector<int> v1, const std::vector<int> v2)
{
return v1.size() - v2.size();
}

int main()
{
std::vector<int> v1(3), v2(1);
int distance(std::vector<int> v1, std::vector<int> v2);
int d = distance(v1, v2);
return 0;
}

The error messages from gcc 3.4.4 or on-line Comeau vaguely
hint that the distance() function template from STL is chosen
instead of the user-defined ordinary function with the same
name.

In this example, does Koenig lookup have higher priority over
the ordinary lookup, i.e. the latter never comes into play?
Or have the ordinary lookup actually been done but other further
rules excluded the locally declared and globally defined distance()
function? If ordinary lookup has really been considered, then the
two distance() functions should become overload candidates and
the user-defined ordinary function with exact match of arguments
should have been chosen rather than the function template. Right?

This is Comeau's diagnostics:

"stl_iterator_base.h", line 110: error:
class "std::vector<int, std::allocator<int>>" has no
member "iterator_category"
typedef typename _Iterator::iterator_category iterator_category;
^
detected during instantiation of class
"std::iterator_traits<_Iterator>
[with _Iterator=std::vector<int, std::allocator<int>>]"
at line 12 of "ComeauTest.c"
------------------------------------
Thanks a lot for your kind enlightenment.

Khoguan Phuann


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

tpo...@mail.ru

unread,
Jul 29, 2005, 5:51:56 AM7/29/05
to
Hallo.

Khoguan Phuann писал(а):

> I have been searching for Koenig lookup examples on the Net.
> Though many of them were found, none of them seems to address
> the following situation:
>
> #include <vector>
>
> int distance(std::vector<int> v1, const std::vector<int> v2)
> {
> return v1.size() - v2.size();
> }
>
> int main()
> {
> std::vector<int> v1(3), v2(1);
> int distance(std::vector<int> v1, std::vector<int> v2);
> int d = distance(v1, v2);
> return 0;
> }

It's not a Koenig lookup problem. Ordinary lookup can find you specific
distance. ADL (or Koenig lookup)
can find std::distance (you need first #include <algorithm>). Overload
resolution selects std::distance, and
here is an error, because vector is not an iterator.

tpo...@mail.ru

unread,
Jul 29, 2005, 5:52:41 AM7/29/05
to
In your example ordinary lookup finds your specific distance, and
Koenig lookup
finds std::distance. But std::distance is:

template <class InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last);

so, compilation fails here (trying to instantiate iterator_traits with
std::vector<int>).

This is simplified version of your code:

namespace STD
{
class A{};
template<class T>
struct Traits
{
typedef typename T::type type;
};

template<class T>
typename Traits<T>::type fun(T &t){}
}

void fun(STD::A){}

int main()
{
STD::A a;
void fun(STD::A);
fun(a);
}

If we modify code

#include <iostream>

namespace STD
{
class A{};
template<class T>
void fun(T){std::cout<<"from STD\n";}
}

void fun(STD::A)
{
std::cout<<"Global\n";
}

int main()
{
void fun(STD::A);
STD::A a;
fun(a);//compiler has two functions, but it selects non-template
(C++ rule)

Jason Hise

unread,
Jul 29, 2005, 7:05:20 AM7/29/05
to
I suspect that the fact that your global distance function takes a
const vector for its second argument may be influencing things. Does
the same thing happen when that const is removed, or when you pass an
actual constant vector for the second argument?

Tom Widmer

unread,
Jul 29, 2005, 8:48:44 AM7/29/05
to
I think this might indicate a widespread compiler bug. By my reckoning,
your global function should be chosen, since template argument
deduction should fail for std::distance

"Type deduction may fail for the following reasons:
[...]
- Attempting to use a type in the qualifier portion of a qualified
name that names a type when that type does not contain the specified
member, or if the specified member is not a type where a type is
required."

That leaves only one function to call (which is found by ordinary
lookup), and that is your distance function, so it should be called
unambiguously. Perhaps someone can point out the flaw in my reasoning,
since it does seem unlikely that VC7.1, GCC 3.4 and Comeau C++ all
share this "bug"!

Tom

Rob Williscroft

unread,
Jul 29, 2005, 8:47:37 AM7/29/05
to
Khoguan Phuann wrote in news:1122581987.748379.43930
@g14g2000cwa.googlegroups.com in comp.lang.c++.moderated:

> #include <vector>
>
> int distance(std::vector<int> v1, const std::vector<int> v2)
> {
> return v1.size() - v2.size();
> }
>
> int main()
> {
> std::vector<int> v1(3), v2(1);
> int distance(std::vector<int> v1, std::vector<int> v2);
> int d = distance(v1, v2);
> return 0;
> }
>
> The error messages from gcc 3.4.4 or on-line Comeau vaguely
> hint that the distance() function template from STL is chosen
> instead of the user-defined ordinary function with the same
> name.
>
> In this example, does Koenig lookup have higher priority over
>

ADL (Argument Dependant Lookup, what you call Koenig lookup)
finds std::distance, and since it is an exact match it is selected
in preference to your ::distance.

The probem here is that <vector> has included a declaration for
std::distance (which its allowed to do) and that the container
std::vector is declared in the same namespace as the alogorithm
std::distance which it isn't designed to work with.

Its a bug in the specification of the Standard library (*) that we
have to live with and the only fix is to make either a qualified
call to your distance function or to forbid ADL:

int main()
{
std::vector<int> v1(3), v2(1);

int d = ::distance(v1, v2); /* qualified */
d = (distance)(v1, v2); /* ADL forbiden */
}

*) The Standard Library was AIUI specifed (and part of the draft
standard) *before* Andrew Koenig invented ADL.

Rob.
--
http://www.victim-prime.dsl.pipex.com/

ka...@gabi-soft.fr

unread,
Jul 29, 2005, 9:45:37 AM7/29/05
to
Khoguan Phuann wrote:
> I have been searching for Koenig lookup examples on the Net.
> Though many of them were found, none of them seems to address
> the following situation:

There's no Koenig lookup involved here.

> #include <vector>

> int distance(std::vector<int> v1, const std::vector<int> v2)
> {
> return v1.size() - v2.size();
> }

> int main()
> {
> std::vector<int> v1(3), v2(1);
> int distance(std::vector<int> v1, std::vector<int> v2);
> int d = distance(v1, v2);
> return 0;
> }

> The error messages from gcc 3.4.4 or on-line Comeau vaguely
> hint that the distance() function template from STL is chosen
> instead of the user-defined ordinary function with the same
> name.

> In this example, does Koenig lookup have higher priority over
> the ordinary lookup, i.e. the latter never comes into play?

Not that I know of. In fact, I would have thought that, having
found a function declaration at block scope, no functions at
namespace scope would be taken into account, Koenig lookup or
not. This would seem to be covered by §3.3.7/1. On the other
hand, §3.4.2 doesn't seem to allow any exceptions, so maybe it
is the intent that std:: also be brought into play. Given that,
however, the locally declared function should still participate
in overload resolution, and given that it is an exact match, I
can hardly see why it wouldn't be chosen.

> Or have the ordinary lookup actually been done but other
> further rules excluded the locally declared and globally
> defined distance() function? If ordinary lookup has really
> been considered, then the two distance() functions should
> become overload candidates and the user-defined ordinary
> function with exact match of arguments should have been chosen
> rather than the function template. Right?

One would hope so.

There is an exception for dependant names in a template, but
that shouldn't apply here.

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

Alf P. Steinbach

unread,
Jul 29, 2005, 9:46:47 AM7/29/05
to
* Khoguan Phuann:

> I have been searching for Koenig lookup examples on the Net.
> Though many of them were found, none of them seems to address
> the following situation:
>
> #include <vector>
>
> int distance(std::vector<int> v1, const std::vector<int> v2)
> {
> return v1.size() - v2.size();
> }
>
> int main()
> {
> std::vector<int> v1(3), v2(1);
> int distance(std::vector<int> v1, std::vector<int> v2);
> int d = distance(v1, v2);
> return 0;
> }
>
> The error messages from gcc 3.4.4 or on-line Comeau vaguely
> hint that the distance() function template from STL is chosen
> instead of the user-defined ordinary function with the same
> name.

I reproduced the error with on-line Comeau and MSVC 7.1 using the following
"corrected" code:

#include <vector>

int distance(
std::vector<int> const& v1, std::vector<int> const& v2
)
{
return static_cast<int>( v1.size() - v2.size() );
}

int main()
{
std::vector<int> v1(3), v2(1);

int d = distance(v1, v2);
return 0;
}

> In this example, does Koenig lookup have higher priority over
> the ordinary lookup, i.e. the latter never comes into play?

Koenig lookup is applied, because the name 'distance' is unqualified.

The additional results of Koenig lookup don't have higher priority.

Quoting §3.4.2/2: "If the ordinary unqualified lookup of the name finds the
declaration of a class member function, the associated namespaces and
classes are not considered. Otherwise the set of declarations found by the
lookup of the function name is the union of the set of declarations found
using ordinary lookup and the set of declarations found in the namespaces
and classes associated with the argument types."

So, only in the case where a class member function is found does the
ordinary lookup have priority.

I wonder why that priority was so restricted.


> Or have the ordinary lookup actually been done but other further
> rules excluded the locally declared and globally defined distance()
> function?

They're on equal footing.


> If ordinary lookup has really been considered, then the
> two distance() functions should become overload candidates and
> the user-defined ordinary function with exact match of arguments
> should have been chosen rather than the function template. Right?

There is the question, yes, but I don't know the answer.

However, it seems that now three compilers, namely gcc, msvc and comeau,
agree that the function template is the best match, and that SFINAE rules
(substitution failure is not an error) don't apply -- whatever the details
of them are, exactly.

With my current understanding I think this -- that ordinary lookup doesn't
have priority in general, only for class member functions -- should be
corrected, because when only the compiler can figure out what's going on for
a simple function call, the language rules are just plain wrong. However,
it may be that someone explains why that restriction to class member
functions was put in there, and that the explanation makes good sense. In
that case my fallback opinion (;-)) is that _something_ should be corrected.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Gianluca Silvestri

unread,
Jul 29, 2005, 9:50:20 AM7/29/05
to

<tpo...@mail.ru> ha scritto nel messaggio
news:1122626681....@g43g2000cwa.googlegroups.com...

| In your example ordinary lookup finds your specific distance, and
| Koenig lookup
| finds std::distance. But std::distance is:
|
| template <class InputIterator>
| typename iterator_traits<InputIterator>::difference_type
| distance(InputIterator first, InputIterator last);
|
| so, compilation fails here (trying to instantiate iterator_traits with
| std::vector<int>).

I can't understand why SFINAE doesn't kick in. If the compiler can't
instantiate the template function with those parameters, why does'nt it
simply remove the template from the set of overloaded functions. ?

Can you explain this, please?

Thanks,
Gianluca

Gianluca Silvestri

unread,
Jul 29, 2005, 9:51:11 AM7/29/05
to

"Rob Williscroft" <r...@freenet.co.uk> ha scritto nel messaggio
news:Xns96A27DC56B203rt...@216.196.109.145...

| Khoguan Phuann wrote in news:1122581987.748379.43930
| @g14g2000cwa.googlegroups.com in comp.lang.c++.moderated:
|
| > #include <vector>
| >
| > int distance(std::vector<int> v1, const std::vector<int> v2)
| > {
| > return v1.size() - v2.size();
| > }
| >
| > int main()
| > {
| > std::vector<int> v1(3), v2(1);
| > int distance(std::vector<int> v1, std::vector<int> v2);
| > int d = distance(v1, v2);
| > return 0;
| > }
| >
| > The error messages from gcc 3.4.4 or on-line Comeau vaguely
| > hint that the distance() function template from STL is chosen
| > instead of the user-defined ordinary function with the same
| > name.
| >
| > In this example, does Koenig lookup have higher priority over
| >
|
| ADL (Argument Dependant Lookup, what you call Koenig lookup)
| finds std::distance, and since it is an exact match it is selected
| in preference to your ::distance.
|
if your remove the const on the second parameter of the OP's distance() you
obtain a perfect match but still I got the error. Do you why?

Thanks,
Gianluca

Rob Williscroft

unread,
Jul 29, 2005, 11:59:36 AM7/29/05
to
Gianluca Silvestri wrote in news:v6qGe.17577$2U1.1...@news3.tin.it in
comp.lang.c++.moderated:

>| > int distance(std::vector<int> v1, const std::vector<int> v2)
>| > {
>| > return v1.size() - v2.size();
>| > }
>| >
>| > int main()
>| > {
>| > std::vector<int> v1(3), v2(1);
>| > int distance(std::vector<int> v1, std::vector<int> v2);
>| > int d = distance(v1, v2);
>| > return 0;
>| > }
>| >

>|


>| ADL (Argument Dependant Lookup, what you call Koenig lookup)
>| finds std::distance, and since it is an exact match it is selected
>| in preference to your ::distance.
>|
> if your remove the const on the second parameter of the OP's
> distance() you obtain a perfect match but still I got the error. Do
> you why?
>

Sorry, I perhapse wasn't clear enough in the above paragraph.

If *any* viable function is found by ADL then functions that
could be found by ordinary lookup aren't considered unless
they are also found by ADL.

In the above case ADL only looks in namespace std (as the
class-template vector is decared there) and nowhere else.
So it only adds std::distance<> to the overload set.
std::distance< std::vector< int > > failes to compile and
a diagnostic is produced.

Also the const applied to non-reference paramiter doesn't
affect the signature of the function as far as overload
resolution is conserned. It only affects wether or not the
argument is treated as const *within* the body of the function.

Rob.
--
http://www.victim-prime.dsl.pipex.com/

Tom Widmer

unread,
Jul 29, 2005, 1:40:12 PM7/29/05
to
It's a perfect match even if you don't remove the const - top level
cv-qualifiers on function parameter types are ignored during overload
resolution.

Tom

Rob Williscroft

unread,
Jul 29, 2005, 1:42:16 PM7/29/05
to
Rob Williscroft wrote in news:Xns96A2A52C853A7rtwfreenetREMOVEcouk@
216.196.109.145 in comp.lang.c++.moderated:

> If *any* viable function is found by ADL then functions that
> could be found by ordinary lookup aren't considered unless
> they are also found by ADL.
>

Why I would think the above is true, I can't imagine. I've read
the relevent portions of the standard several times, clearly
though, not yet enough times for it to actually sink in ;-(.

Alf P. Steinbach

unread,
Jul 29, 2005, 1:41:18 PM7/29/05
to
* Rob Williscroft:

> Gianluca Silvestri wrote in news:v6qGe.17577$2U1.1...@news3.tin.it in
> comp.lang.c++.moderated:
>
> >| > int distance(std::vector<int> v1, const std::vector<int> v2)
> >| > {
> >| > return v1.size() - v2.size();
> >| > }
> >| >
> >| > int main()
> >| > {
> >| > std::vector<int> v1(3), v2(1);
> >| > int distance(std::vector<int> v1, std::vector<int> v2);
> >| > int d = distance(v1, v2);
> >| > return 0;
> >| > }
> >| >
>
> >|
> >| ADL (Argument Dependant Lookup, what you call Koenig lookup)
> >| finds std::distance, and since it is an exact match it is selected
> >| in preference to your ::distance.
> >|
> > if your remove the const on the second parameter of the OP's
> > distance() you obtain a perfect match but still I got the error. Do
> > you why?
> >
>
> Sorry, I perhapse wasn't clear enough in the above paragraph.
>
> If *any* viable function is found by ADL then functions that
> could be found by ordinary lookup aren't considered unless
> they are also found by ADL.
>
> In the above case ADL only looks in namespace std (as the
> class-template vector is decared there) and nowhere else.
> So it only adds std::distance<> to the overload set.
> std::distance< std::vector< int > > failes to compile and
> a diagnostic is produced.

Quoting §3.4.2/2: "If the ordinary unqualified lookup of the name finds the


declaration of a class member function, the associated namespaces and
classes are not considered. Otherwise the set of declarations found by the
lookup of the function name is the union of the set of declarations found
using ordinary lookup and the set of declarations found in the namespaces
and classes associated with the argument types."

Note: "union".

I don't find your rule, and the above seems to contradict it?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Rob Williscroft

unread,
Jul 29, 2005, 1:48:21 PM7/29/05
to
Khoguan Phuann wrote in news:1122581987.748379.43930
@g14g2000cwa.googlegroups.com in comp.lang.c++.moderated:

>

> #include <vector>
>
> int distance(std::vector<int> v1, const std::vector<int> v2)
> {
> return v1.size() - v2.size();
> }
>
> int main()
> {
> std::vector<int> v1(3), v2(1);
> int distance(std::vector<int> v1, std::vector<int> v2);
> int d = distance(v1, v2);
> return 0;
> }
>
> The error messages from gcc 3.4.4 or on-line Comeau vaguely
> hint that the distance() function template from STL is chosen
> instead of the user-defined ordinary function with the same
> name.
>
>

Having got this wrong once (or twice) I'll have another go,

The problem isn't that std::distance is being choosen above
::distance, but that overload resolution failes to complete.

Here's the declaration for std::distance:

template<class InIt> inline
typename iterator_traits<InIt>::difference_type
distance( InIt First, InIt Last);

Before overload resoution can proceed, the compiler tries to
instantiate this (declaration!) with InIt = std::vector< int >.

Inorder to do that it needs to temporarily instantiate
std::iterator_traits< std::vector< int > >, this unsuprisingly
fails and a diagnostic is produced.

Alas this isn't a substitution failure, had iterator_traits
instantiated but lacked the type(def) member difference_type,
that would be a substitution failure. I.e. the substitution
would compile but *fail* to provide a valid type where one
was expected.

Rob.
--
http://www.victim-prime.dsl.pipex.com/

Khoguan Phuann

unread,
Jul 29, 2005, 1:49:50 PM7/29/05
to
Khoguan's original code:

> #include <vector>
> int distance(std::vector<int> v1, const std::vector<int> v2)
^^^^^
const should be removed to test perfect match

> {
> return v1.size() - v2.size();
> }
> int main()
> {
> std::vector<int> v1(3), v2(1);
> int distance(std::vector<int> v1, std::vector<int> v2);
> int d = distance(v1, v2);
> return 0;
> }

Jason Hise wrote:
> I suspect that the fact that your global distance function takes a
> const vector for its second argument may be influencing things. Does
> the same thing happen when that const is removed, or when you pass an
> actual constant vector for the second argument?

Oops! I am sorry for this distracting typo of mine.
I knew that the efficient and common-sense practice
for this kind of function is to declare its parameters
as T const&. That was exactly the way I once did it in
one of my experiments for this problem. But I wanted to
enforce a perfect match for my non-template function
to see if I could please the compilers to choose it. So
I shoud have removed both const qualifiers from my function
parameters. But the compilers still don't prefer it. :(

I really appreciate all of the gurus' responses here to
my problem. Though it looks like a definitive resolution
remains to be achieved. Specifically:

1. Why doesn't SFINAE apply?

2. Why doesn't the non-template, locally declared function
with perfect match take precedence over the function template?

The error messages from GCC, Comeau and MSVC seem
to show that they unanimously just ignore the non-template
function in the first place. Would anyone from the compiler
teams please to refer me to some paragraph numbers of the
standard which prescribe this rather non-intuitive behavior?
Then I might have a chance to further think about its rationale :-)

Best regards,
Khoguan Phuann

Gene Bushuyev

unread,
Jul 29, 2005, 8:34:05 PM7/29/05
to
"Khoguan Phuann" <kho...@ms4.hinet.net> wrote in message
news:1122581987....@g14g2000cwa.googlegroups.com...

>I have been searching for Koenig lookup examples on the Net.
> Though many of them were found, none of them seems to address
> the following situation:
>
> #include <vector>
>
> int distance(std::vector<int> v1, const std::vector<int> v2)
> {
> return v1.size() - v2.size();
> }
>
> int main()
> {
> std::vector<int> v1(3), v2(1);
> int distance(std::vector<int> v1, std::vector<int> v2);
> int d = distance(v1, v2);
> return 0;
> }
>
> The error messages from gcc 3.4.4 or on-line Comeau vaguely
> hint that the distance() function template from STL is chosen
> instead of the user-defined ordinary function with the same
> name.

I think the answer to your question is that compilers are pretty broken when
it comes to ADL and overloading. I did 3 experiments, all of which I believe
are well formed, and yet compilers are failing:

1) simplified original test failed on comeau online, vc8b2, gcc3.3.1:

#include <vector>

int distance(const std::vector<int>& v1,
const std::vector<int>& v2);

int my_test()
{
std::vector<int> v;
return distance(v, v);
}
2) simple ADL failed on comeau online, passed on vc8b2, passed on gcc3.3.1:

#include <vector>

int my_test()
{
std::vector<int> v;
return distance(v.begin(), v.end());
}
3) "distracted" ADL failed on comeau online, passed on vc8b2, passed on
gcc3.3.1:

#include <vector>

int distance(const std::vector<int>& v1,
const std::vector<int>& v2);

int my_test()
{
std::vector<int> v;
return distance(v.begin(), v.end());
}

- gene

Maxim Yegorushkin

unread,
Jul 29, 2005, 8:33:40 PM7/29/05
to
Khoguan Phuann wrote:
> I have been searching for Koenig lookup examples on the Net.
> Though many of them were found, none of them seems to address
> the following situation:
>
> #include <vector>
>
> int distance(std::vector<int> v1, const std::vector<int> v2)
> {
> return v1.size() - v2.size();
> }
>
> int main()
> {
> std::vector<int> v1(3), v2(1);
> int distance(std::vector<int> v1, std::vector<int> v2);
> int d = distance(v1, v2);
> return 0;
> }
>
> The error messages from gcc 3.4.4 or on-line Comeau vaguely
> hint that the distance() function template from STL is chosen
> instead of the user-defined ordinary function with the same
> name.
>
> In this example, does Koenig lookup have higher priority over
> the ordinary lookup, i.e. the latter never comes into play?
> Or have the ordinary lookup actually been done but other further
> rules excluded the locally declared and globally defined distance()
> function? If ordinary lookup has really been considered, then the
> two distance() functions should become overload candidates and
> the user-defined ordinary function with exact match of arguments
> should have been chosen rather than the function template. Right?

Your conclusions are wrong.

What is really happening here is that a compiler is building a set of
viable functions for distance() unqualified call. Using ADL it finds
std::distance. The declaration of std::distance is well-formed, so
SFINAE does not apply here, since std::iterator_traits provides the
typedef used in std::distance declaration, although the nested typedef
itself is ill-formed for std::vector. So, the error here is produced by
the typedef ill-formedness encountered at the stage of building the set
of viable functions, rather than selecting one.

To make it more clear you can specialize std::iterator_traits for
vector, so that it provides the necessary well-formed typedefs, what
makes building the set not to end up with an error at std::distance
declaration. As you will see, the proper overload is choosen then -
your custom distance.

#include <vector>
#include <iostream>


int distance(
std::vector<int> const& v1, std::vector<int> const& v2
)
{

std::cout << __FUNCSIG__ << std::endl;


return static_cast<int>( v1.size() - v2.size() );
}

// this is a hack
namespace std {


// this makes std::distance declaration well formed
template<>
struct iterator_traits<vector<int> >
{
typedef int iterator_category;
typedef int value_type;
typedef int difference_type;
typedef int distance_type;
typedef int pointer;
typedef int reference;
};

}

int main()
{
std::vector<int> v1(3), v2(1);

int d = distance(v1, v2);
return 0;
}

tpo...@mail.ru

unread,
Jul 29, 2005, 8:31:54 PM7/29/05
to
> I really appreciate all of the gurus' responses here to
> my problem. Though it looks like a definitive resolution
> remains to be achieved. Specifically:
>
> 1. Why doesn't SFINAE apply?

May be, because there is no SFINAE here? SFINAE == substitution failure
is not an error,
but there is no substitution failure here, we have
iterator_traits<int>::lalala and during instantiation of
iterator_traits we
have an error?

>
> 2. Why doesn't the non-template, locally declared function
> with perfect match take precedence over the function template?

Because during the instantiation we get an error?

>
> The error messages from GCC, Comeau and MSVC seem
> to show that they unanimously just ignore the non-template
> function in the first place.

They do not ignore it!!! If you try

namespace STD{
template<class T>
void fun(T t){}
struct A{};
}

void fun(STD::A a){}

int main()
{
void fun(STD::A);
STD::A a;

fun(a); //non-template function will be selected!!! NOT IGNORED!!!

ka...@gabi-soft.fr

unread,
Aug 1, 2005, 5:39:18 AM8/1/05
to
Gianluca Silvestri wrote:
> <tpo...@mail.ru> ha scritto nel messaggio
> news:1122626681....@g43g2000cwa.googlegroups.com...
> | In your example ordinary lookup finds your specific
> | distance, and Koenig lookup finds std::distance. But
> | std::distance is:

> | template <class InputIterator>
> | typename iterator_traits<InputIterator>::difference_type
> | distance(InputIterator first, InputIterator last);

> | so, compilation fails here (trying to instantiate
> | iterator_traits with std::vector<int>).

> I can't understand why SFINAE doesn't kick in. If the compiler
> can't instantiate the template function with those parameters,
> why does'nt it simply remove the template from the set of
> overloaded functions. ?

> Can you explain this, please?

I'm not sure of all the details, but roughly speaking, the
compiler doesn't try to instantiate the complete function until
the function has been choses by overload resolution.
Intuitively, SFINAE only applies to errors occuring when
instantiating the function declaration.

Of course, that still doesn't explain why the exact match of a
non-template function wasn't found and preferred. (The rule is
that if a template instantiation and a non template function are
equal matches, the non template function wins. In his case, the
non template function was an exact match, so the template
function can't be better.)

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

ka...@gabi-soft.fr

unread,
Aug 1, 2005, 6:26:29 AM8/1/05
to

> > #include <vector>

> #include <vector>

Which means that his global function should be in the overload
set.

> Quoting §3.4.2/2: "If the ordinary unqualified lookup of the
> name finds the declaration of a class member function, the
> associated namespaces and classes are not
> considered. Otherwise the set of declarations found by the
> lookup of the function name is the union of the set of
> declarations found using ordinary lookup and the set of
> declarations found in the namespaces and classes associated
> with the argument types."

> So, only in the case where a class member function is found
> does the ordinary lookup have priority.

> I wonder why that priority was so restricted.

Probably because it's hard to make sense of what p.f(a) might
mean if f were found in a namespace dependant on a:-). And I
suspect that in a member function of class C, if class C has a
function f, it would probably surprise people if f(a) didn't
call that function.

> > Or have the ordinary lookup actually been done but other
> > further rules excluded the locally declared and globally
> > defined distance() function?

> They're on equal footing.

> > If ordinary lookup has really been considered, then the two
> > distance() functions should become overload candidates and
> > the user-defined ordinary function with exact match of
> > arguments should have been chosen rather than the function
> > template. Right?

> There is the question, yes, but I don't know the answer.

> However, it seems that now three compilers, namely gcc, msvc
> and comeau, agree that the function template is the best
> match, and that SFINAE rules (substitution failure is not an
> error) don't apply -- whatever the details of them are,
> exactly.

It's hard to say. Either they consider it the best match, or
they are instantiating it simply because it is part of the
overload set. There seem to be a number of contradictory
statements about this in §14.7.1:

In paragraph 2:
Unless a function template specialization has been
explicitly instantiated or explicitly specialized, the
function template specialization is implicitly instantiated
when the specialization is referenced in a context that
requires a function definition to exist.

Only a declaration, not a definition, is required here.

In paragraph 8:
If a function template or a member function template
specialization is used in a way that involves overload
resolution, a declaration of the specialization is
implicitly instantiated.

What does it mean, "in a way that involves overload resolution"?
Presumably, here, one of the instations is part of the overload
set. Presumably, in fact, all possible instantiations are in
fact part of the overload set. I'm guessing that those
functions not instantiated by template argument deduction are
not considered (and that this is where SFINAE comes into play).

In paragraph 9:

An implementation shall not implicitly instantiate a
function template, a member template, a non-virtual member
function, a member class or a static data member of a class
template that does not require instantiation.

That, in conjunction with paragraph 2, would seem to make it
explicitly clear that the compiler may *NOT* instantiate the
template function found by ADL. Except that I think that
paragraph 8 is saying that the implementation *MUST* instantiate
the function here. (And the wording in paragraph 2 doesn't seem
to make it exclusive -- it specifies *one* case where the
template must be instantiated, but doesn't exclude the existance
of others).

Anyway, the solution for the original poster is obvious: he must
provide a specialization of std::iterator_traits, and all of the
necessary operators for std::vector<int>, and his code should
work:-). And of course, declarations should suffice here -- he
doesn't need to actually implement the code for
operator++(std::vector<int>); just provide a declaration that
the compiler can find. Which means that it must be in std:: --
which in turn means that he is dealing with undefined behavior,
but this is an undefined behavior that we know will work --
unless the actual implementation defines this operator for some
internal purposes.

Yuck. IMHO, the original poster is ill adviced in his choice of
a name, and the obvious solution is really just to use a
different name for the function. But logically, no one can
really be expected to know and avoid all of the function names
defined in std::. And part of the goal of namespaces, I would
have thought, is that you don't have to in order to avoid
conflicts.

> With my current understanding I think this -- that ordinary
> lookup doesn't have priority in general, only for class member
> functions -- should be corrected, because when only the
> compiler can figure out what's going on for a simple function
> call, the language rules are just plain wrong. However, it
> may be that someone explains why that restriction to class
> member functions was put in there, and that the explanation
> makes good sense. In that case my fallback opinion (;-)) is
> that _something_ should be corrected.

Unless I'm mistaken, ADL was first thought of as applying to
overloaded operators. For example, if z1 and z2 are
std::complex<double>, you want operator overloading to take into
account std::operator+( std::complex<double>,
std::complex<double>) even if non ADL finds some operator+ in
global namespace (say like operator+(double, double)).

Presumably, the same thing should apply to named functions.
Except that if you call a class member function, you don't
really expect ADL to pull non-member functions into the overload
set.

That seems sort of reasonable to me (in so far as anything
involving ADL is reasonable). On the other hand, I'd really
like an explination as to why the compiler should instantiate a
template function definition simply because the instantiation is
part of the overload set. I would have thought that the
declaration would have sufficed, and that if the compiler then
chose some other function, the definition would not have been
instantiated. I sort of think that this should be changed.

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

Gianluca Silvestri

unread,
Aug 1, 2005, 7:44:48 AM8/1/05
to

<ka...@gabi-soft.fr> ha scritto nel messaggio
news:1122883588....@o13g2000cwo.googlegroups.com...

| Gianluca Silvestri wrote:
| > <tpo...@mail.ru> ha scritto nel messaggio
| > news:1122626681....@g43g2000cwa.googlegroups.com...
| > | In your example ordinary lookup finds your specific
| > | distance, and Koenig lookup finds std::distance. But
| > | std::distance is:
|
| > | template <class InputIterator>
| > | typename iterator_traits<InputIterator>::difference_type
| > | distance(InputIterator first, InputIterator last);
|
| > | so, compilation fails here (trying to instantiate
| > | iterator_traits with std::vector<int>).
|
| > I can't understand why SFINAE doesn't kick in. If the compiler
| > can't instantiate the template function with those parameters,
| > why does'nt it simply remove the template from the set of
| > overloaded functions. ?
|
| > Can you explain this, please?
|
| I'm not sure of all the details, but roughly speaking, the
| compiler doesn't try to instantiate the complete function until
| the function has been choses by overload resolution.
| Intuitively, SFINAE only applies to errors occuring when
| instantiating the function declaration.

TTBOMB the complete sequence the compiler is involved in when dealing with a
(probable) unqualified function call is:
1. Lookup names. Say the compiler finds both functions and function
templates with the same name
2. for the function templates the compiler tries to deduce template
parameter. If for some function template the deduction those are removed
from the overload set. Here SFINAE takes place
3. For the remaining elements in the set it chooses the viable functions,
based roughly on the number of parameters.
4. Finally it chooses the best viable function, if there is one.

So the compiler has to try to instantiate the function declaration in order
to find if that function template is eligible for overload resolution.

So again I don't see why the OP's example fails to compile. There must be
something I can't see.


----
Gianluca Silvestri

Gianluca Silvestri

unread,
Aug 1, 2005, 8:01:58 AM8/1/05
to

| That seems sort of reasonable to me (in so far as anything
| involving ADL is reasonable). On the other hand, I'd really
| like an explination as to why the compiler should instantiate a
| template function definition simply because the instantiation is
| part of the overload set. I would have thought that the
| declaration would have sufficed, and that if the compiler then
| chose some other function, the definition would not have been
| instantiated. I sort of think that this should be changed.
|

All the problem is originated by the the fact that:
1. standard doesn't mandate the dependencies between header files, so the
OP's library implementation, when including <vector>, also includes
<iterator>
2. the declaration of std::distance in <iterator> is:


template<class InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last);

So the compiler, once found the templated distance(), tries to deduce the
template parameters to see if it can add the an instantiation of the
function to the overload set. In doing so it succeeds in the substitution of
InputIterator with std::vector<int>. Then it tries to instantiate
std::iterator_traits<vector<int> >, and it find the primary template
definitionl. Where it fails is when it looks for the member
std::vector<int>::difference_type, defined as typedef in
std::iterator_traits<std::vector<int> >. And here is finally the *big*
question:
Is this failure part of the template deduction machanism or not ? the
compilers tested seems to answer no to this question. I'm no expert, but to
me the answer should be yes.

---
Gianluca Silvestri

john...@yahoo.com

unread,
Aug 2, 2005, 6:17:41 AM8/2/05
to
ka...@gabi-soft.fr wrote:
> I would have thought that the
> declaration would have sufficed, and that if the compiler then
> chose some other function, the definition would not have been
> instantiated.

Perhaps I'm off-base here, but it looks to me as if the error occurs
upon the instantiation of the declaration. Doesn't instantiation of
the function declaration require instantiating the function's return
type? And isn't that what fails?

I'd say that the real source of the problem is that
std::iterator_traits<T> is defined in a manner quite unlike the typical
traits class template. Most commonly, the "default" implementation of
a traits class template either provides some reasonable default value
or type, or is empty. In either case, it's possible to instantiate the
trait for any arbitrary type -- though using it may fail (in the case
of the empty implemention).

I suspect that the reason for this design decision was to allow
std::iterator_traits to work with iterator types which are member types
of containers. (Otherwise, you'd have to partially specialize
iterator_traits on a member name of a template...which is not possible.)

ka...@gabi-soft.fr

unread,
Aug 2, 2005, 6:39:51 AM8/2/05
to
Gianluca Silvestri wrote:
> | That seems sort of reasonable to me (in so far as anything
> | involving ADL is reasonable). On the other hand, I'd really
> | like an explination as to why the compiler should
> | instantiate a template function definition simply because
> | the instantiation is part of the overload set. I would have
> | thought that the declaration would have sufficed, and that
> | if the compiler then chose some other function, the
> | definition would not have been instantiated. I sort of
> | think that this should be changed.

> All the problem is originated by the the fact that:
> 1. standard doesn't mandate the dependencies between header
> files, so the OP's library implementation, when including
> <vector>, also includes <iterator>

IMHO, that's a false problem here. Even if the standard didn't
allow <vector> to include <iterator>, a later maintenance
programmer might require something from <iterator>. Code that
only works if <iterator> is not included is too brittle.

> 2. the declaration of std::distance in <iterator> is:
> template<class InputIterator>
> typename iterator_traits<InputIterator>::difference_type
> distance(InputIterator first, InputIterator last);

> So the compiler, once found the templated distance(), tries to
> deduce the template parameters to see if it can add the an
> instantiation of the function to the overload set. In doing so
> it succeeds in the substitution of InputIterator with
> std::vector<int>. Then it tries to instantiate
> std::iterator_traits<vector<int> >, and it find the primary
> template definitionl. Where it fails is when it looks for the
> member std::vector<int>::difference_type, defined as typedef
> in std::iterator_traits<std::vector<int> >.

IMHO, this is where something should be done. I don't see any
reason for the compiler to instantiate the definition of the
function unless it is actually chosen by function overload
resolution.

> And here is finally the *big* question: Is this failure part
> of the template deduction machanism or not ? the compilers
> tested seems to answer no to this question. I'm no expert, but
> to me the answer should be yes.

I think that the answer is definitly no. Argument deduction
only involves the function declaration, not its definition. So
it works. IMHO, the "error" (in the standard) is then requiring
(or even allowing) the instantiation of the definition when the
function is not chosen by function overload resolution.

Note that in the non-template case, if you provide several
overloaded function declarations, you are only required to
provide a definition for those functions which are actually
called. Instantiating the template definition provides a
definition, and should logically only occur if the definition is
needed.

Maybe someone who is familiar with the standard's position on
templates could clarify the issue: there must be a reason behind
what the standard requires.

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

Gianluca Silvestri

unread,
Aug 2, 2005, 8:53:24 AM8/2/05
to
[snip]

| IMHO, this is where something should be done. I don't see any
| reason for the compiler to instantiate the definition of the
| function unless it is actually chosen by function overload
| resolution.
|
| > And here is finally the *big* question: Is this failure part
| > of the template deduction machanism or not ? the compilers
| > tested seems to answer no to this question. I'm no expert, but
| > to me the answer should be yes.
|
| I think that the answer is definitly no. Argument deduction
| only involves the function declaration, not its definition. So
| it works. IMHO, the "error" (in the standard) is then requiring
| (or even allowing) the instantiation of the definition when the
| function is not chosen by function overload resolution.

So you agree that there is no deduction failure but a true error in template
instantiation. Bu then you want the error to be ignored because that
template wouldn't be chosen anyway from overload resolution. If it is what
you meant I totally agree with you but the problem that the Standard should
review the overload process regarding templates. During overload resolution
a template might produce an error during istantiation but the compiler must
keep going on until it finds a best match. It the winner of the match is NOT
the template that causes the error all went wll, otherwise it must issue an
error.
It seems like a proposal for a revision, but I think it's too late now.

|
| Note that in the non-template case, if you provide several
| overloaded function declarations, you are only required to
| provide a definition for those functions which are actually
| called. Instantiating the template definition provides a
| definition, and should logically only occur if the definition is
| needed.

But here the problem is in the declaration, not the definition

|
| Maybe someone who is familiar with the standard's position on
| templates could clarify the issue: there must be a reason behind
| what the standard requires.
|

Perhaps a thread on comp.std.c++ would be a better place to discuss these
things

---
Gianluca Silvestri

ka...@gabi-soft.fr

unread,
Aug 2, 2005, 8:57:23 AM8/2/05
to

I'd say that the operation was the reverse. For each
non-template function name, the function (declaration) is added
to the overload set. For each template name, the compiler
attempts template argument deduction. If this succeeds the
corresponding function is instantiated and added to the overload
set.

In fact, I think I misread some of the standard earlier. It is
only the declaration which is instantiated here, and not the
definition. However, in this case, even the declaration
requires iterator_traits, etc. The point remains, however, that
only errors in template argument deduction cause the
instantiation not to be added to the overload set. Once
argument deduction has done its job, we're back into the usual
world where an error is an error. And the return type doesn't
play a role in argument deduction.

(Note that there are a few, very special cases where the return
type plays a role in overload resolution, so the compiler has to
instantiate it.)

> 3. For the remaining elements in the set it chooses the viable
> functions, based roughly on the number of parameters.
> 4. Finally it chooses the best viable function, if there is one.

> So the compiler has to try to instantiate the function
> declaration in order to find if that function template is
> eligible for overload resolution.

> So again I don't see why the OP's example fails to
> compile. There must be something I can't see.

You said it in the previous paragraph. The compiler
instantiates the template function declaration (because no
errors occur during template argument deduction). The function
declaration has a return type of
iterator_traits<InputIterator>::difference_type. Argument
deduction has found vector<int> for InputIterator. Trying to
instantiate iterator_traits over vector<int> causes an error.

I know that there is some discussion in the standards committee
concerning concepts. I wonder if that might help here: from
what little I know, std::distance would require that
InputIterator conform to the InputIterator concept, and of
course, vector<int> will not conform to that concept. But
whether the error is considered to occur in argument deduction,
or at a later time, I don't know.

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

Gianluca Silvestri

unread,
Aug 2, 2005, 10:40:41 AM8/2/05
to
[snip]

| > 3. For the remaining elements in the set it chooses the viable
| > functions, based roughly on the number of parameters.
| > 4. Finally it chooses the best viable function, if there is one.
|
| > So the compiler has to try to instantiate the function
| > declaration in order to find if that function template is
| > eligible for overload resolution.
|
| > So again I don't see why the OP's example fails to
| > compile. There must be something I can't see.
|
| You said it in the previous paragraph. The compiler
| instantiates the template function declaration (because no
| errors occur during template argument deduction). The function
| declaration has a return type of
| iterator_traits<InputIterator>::difference_type. Argument
| deduction has found vector<int> for InputIterator. Trying to
| instantiate iterator_traits over vector<int> causes an error.
|
| I know that there is some discussion in the standards committee
| concerning concepts. I wonder if that might help here: from
| what little I know, std::distance would require that
| InputIterator conform to the InputIterator concept, and of
| course, vector<int> will not conform to that concept. But
| whether the error is considered to occur in argument deduction,
| or at a later time, I don't know.

Concepts are an interesting solution to the problem I didn't think of. Bu
then still there is quetion: given that vector<int> fails to be a model for
the concept InputIterator, does this fact make the compilation fail or
simply it means that the template is discarded from the overload set?


---
Gianluca Silvestri

Tom Widmer

unread,
Aug 2, 2005, 4:26:46 PM8/2/05
to
Compilation fails. Generation of an ill-formed template specialization
(such as iterator_traits<vector<int> > is not one of the cases that
SFINAE allows. If the problem were than iterator_traits<vector<int> >
was well-formed, but that it didn't have a difference_type typedef,
then SFINAE would work, but that isn't the situation here.

Tom

Gene Bushuyev

unread,
Aug 2, 2005, 4:34:35 PM8/2/05
to
<ka...@gabi-soft.fr> wrote in message
news:1122973182....@g47g2000cwa.googlegroups.com...
...

> In fact, I think I misread some of the standard earlier. It is
> only the declaration which is instantiated here, and not the
> definition. However, in this case, even the declaration
> requires iterator_traits, etc. The point remains, however, that
> only errors in template argument deduction cause the
> instantiation not to be added to the overload set. Once
> argument deduction has done its job, we're back into the usual
> world where an error is an error. And the return type doesn't
> play a role in argument deduction.

No, no, no, the return type does play role in template argument deduction.
It's *template* argument deduction, not just function argument deduction.
ADL has nothing really to do with the discussed issues. Try instead this
simplified example that demonstrates the problem:

template<class T>
struct my_traits
#if defined(PROVIDE_DEFINITION)
{
typedef typename T::no_such_type no_such_type;
}
#endif
;

// #1
template<class T>
typename my_traits<T>::no_such_type foo(T);

// #2
template<class T>
typename T::no_such_type foo(T);

// #3
int foo(int);

int my_test()
{
int i = 0;
return foo(i);
}

when function #1 is commented out or only forward declaration of my_traits
is provided (PROVIDE_DEFINITION not defined), compilers have no problem
choosing function #3, because argument deduction in #1 and #2 fails due to
the return type. But when #1 is uncommented and my_traits is actually
defined, compilers failing trying to instantiate my_traits. But the Standard
doesn't make the difference regarding the reason why the argument deduction
fails. If it's failed, the template function is not instantiated and not
participating in overload resolution (14.8.3/1)

- gene

Allan W

unread,
Aug 2, 2005, 7:19:05 PM8/2/05
to
In topic "Koenig lookup hides ordinary function",

ka...@gabi-soft.fr wrote:
> (Note that there are a few, very special cases where the return
> type plays a role in overload resolution, so the compiler has to
> instantiate
[some template functions]
> .)

I've learned that most of what you say is true, so I believe you.
But I find this astonishing -- I wasn't aware that the return type
EVER played a role in overload resolution.

Would you tell me which part of the standard says so, and/or
provide an example? I'd like to learn more about this.

I assume this is not a FAQ? Should it be? Or would it just
confuse beginners?

ka...@gabi-soft.fr

unread,
Aug 3, 2005, 9:10:20 AM8/3/05
to
Gianluca Silvestri wrote:
> [snip]

> | I know that there is some discussion in the standards
> | committee concerning concepts. I wonder if that might help
> | here: from what little I know, std::distance would require
> | that InputIterator conform to the InputIterator concept, and
> | of course, vector<int> will not conform to that concept.
> | But whether the error is considered to occur in argument
> | deduction, or at a later time, I don't know.

> Concepts are an interesting solution to the problem I didn't
> think of. Bu then still there is quetion: given that
> vector<int> fails to be a model for the concept InputIterator,
> does this fact make the compilation fail or simply it means
> that the template is discarded from the overload set?

It depends on what is finally adopted, I imagine. In theory at
least (but I'm not a specialist here), I think that it would be
possible to exclude functions from the overload set due to
concept failure; that concept failure is a case of SFINAE.
Whether the actual proposal does this, or even whether it would
be a good idea, I have no idea.

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

ka...@gabi-soft.fr

unread,
Aug 3, 2005, 9:18:13 AM8/3/05
to
Gene Bushuyev wrote:
> <ka...@gabi-soft.fr> wrote in message
> news:1122973182....@g47g2000cwa.googlegroups.com...
> ...

> > In fact, I think I misread some of the standard earlier. It
> > is only the declaration which is instantiated here, and not
> > the definition. However, in this case, even the declaration
> > requires iterator_traits, etc. The point remains, however,
> > that only errors in template argument deduction cause the
> > instantiation not to be added to the overload set. Once
> > argument deduction has done its job, we're back into the
> > usual world where an error is an error. And the return type
> > doesn't play a role in argument deduction.

> No, no, no, the return type does play role in template
> argument deduction.

Actually, as in function overload resolution, it depends on
context. In the case of a called function, "Template argument
deduction is done by comparing each function template parameter
type (call it P) with the type of the corresponding arugment of
the class (cal it A) as described below." Return type is *not*
used for template argument deduction; at least not directly.

What you are referring to, no doubt, is: "When all template
arguments have been deduced, all uses of template parameters in
nondeduced contexts are replaced with the corresponding deduced
argument values. If a substitution in a template parameter or
in the function type of the function template results in an
invalid type, type deduction fails." which, of course, does
include the return value. In other words, deduction itself is
based only on matching the types of the arguments to the types
of the parameters, but if this deduction results in an illegal
return type, the deduction is deemed to fail.

> It's *template* argument deduction, not just function argument
> deduction.

But the deduction is entirely based on matching the function
argument types to the parameters of the function. As such, it
doesn't take the return type into account, except as a final
validation: was my deduction acceptable.

> ADL has nothing really to do with the discussed issues.

Except that it allows the compiler to find the corresponding
template in name lookup. Agreed.

> Try instead this simplified example that demonstrates the
> problem:

> template<class T>
> struct my_traits
> #if defined(PROVIDE_DEFINITION)
> {
> typedef typename T::no_such_type no_such_type;
> }
> #endif
> ;

> // #1
> template<class T>
> typename my_traits<T>::no_such_type foo(T);

> // #2
> template<class T>
> typename T::no_such_type foo(T);

> // #3
> int foo(int);

> int my_test()
> {
> int i = 0;
> return foo(i);
> }

> when function #1 is commented out or only forward declaration
> of my_traits is provided (PROVIDE_DEFINITION not defined),
> compilers have no problem choosing function #3, because
> argument deduction in #1 and #2 fails due to the return type.

Is this what actually compilers do, or is this what the standard
says? For #2, it's clear: int::no_such_type is not a legal type
expression, so the substitution of the deduced type int triggers
the second clause I cited above, "If a substitution in a
template parameter or in the function type of the function
template results in an invalid type, type deduction fails."
SFINAE, in sum. For #1, if only a forward declaration is
provided, I'm not sure.

My impression is that the compiler must instantiate the template
at this point, but it is far from clear. There are, in fact,
several issues. The first is the point mentionned above: "If a
substitution in a template parameter or in the function type of
the function template results in an invalid type, type deduction
fails." The compiler must make the substitution, and it must
verify that the resulting function type is valid. One of the
reasons it might be invalid is because it is "Attempting to use
a type in the qualifier portion of a qualified name that names a
types when that type does not contain the specified member, or
if the specified member is not a type where a type is required."

On the other hand, the standard says "If the overload resolution
process can determine the correct function to call without
instantiating a class template definition, it is unspecified
whether that instantiation actually takes place." Since there
is a non-template function which is an exact match, overload
resolution can exclude the template functions, regardless of
their return type, so it doesn't need to instantiate this class
template in order to determine the correct function to call.
Which means that whether the code is correct or not is
unspecified. And this also holds for the original problem.

> But when #1 is uncommented and my_traits is actually defined,
> compilers failing trying to instantiate my_traits. But the
> Standard doesn't make the difference regarding the reason why
> the argument deduction fails.

Oh, but it does. If the compiler tries to instantiate
my_traits, and fails, it is an error. The standard lists very
explicitly the cases which are verified after template argument
deduction. Problems in instantiating another template is not in
the list. If the compiler tries to instantiate my_traits,
succeeds, and my_traits<int>::no_such_type isn't defined, or
isn't a type, type substitution fails, and SFINAE kicks in.

After rereading the standard several times here, I think we
actually have a case of unspecified behavior. Since whatever
happens, overload resolution will finally resolve to the
non-template function, it is unspecified whether template
classes involved in the function declaration are instantiated or
not.

In practice, I think that most compilers instantiate if they see
a definition, and skip the instantiation if they don't
(presumably to give an error later if they really do need the
instantiation in overload resolution).

> If it's failed, the template function is not instantiated and
> not participating in overload resolution (14.8.3/1)

If template argument deduction fails, yes. But template
argument deduction is a very complicated subject in itself.

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

ka...@gabi-soft.fr

unread,
Aug 3, 2005, 10:23:41 AM8/3/05
to
Allan W wrote:
> In topic "Koenig lookup hides ordinary function",
> ka...@gabi-soft.fr wrote:
> > (Note that there are a few, very special cases where the
> > return type plays a role in overload resolution, so the
> > compiler has to instantiate
> [some template functions]
> > .)

> I've learned that most of what you say is true, so I believe
> you.

When it comes to templates, or some of the finer issues of
function overload resolution, you're being overly trusting. In
this case, however...

> But I find this astonishing -- I wasn't aware that the return
> type EVER played a role in overload resolution.

> Would you tell me which part of the standard says so, and/or
> provide an example? I'd like to learn more about this.

namespace A
{
double f() ;
}

namespace B
{
int f() ;
}

double (*g())()
{
using namespace A ;
using namespace B ;
return &f ;
}

(Trying to call f from within g results in a compiler error
saying that the call is ambiguous. Regardless of what you do
with the return value.)

The section in the standard is §13.4. Taking the address of an
overloaded function. The key sentence is: "The function
selected is the one whose type matches the target type required
in the context." The type of a function, of course, includes
the return type. (Creating the example was the tricky part,
because normally, just declaring two functions in the same scope
which differ only in return type is illegal.)

> I assume this is not a FAQ? Should it be? Or would it just
> confuse beginners?

Some things are best left unsaid:-). I would be very, very
sceptical of any program which really used this.

You'll have to admit that its an amusing bit of trivia, however.

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

ka...@gabi-soft.fr

unread,
Aug 3, 2005, 10:25:47 AM8/3/05
to
john...@yahoo.com wrote:
> ka...@gabi-soft.fr wrote:

> > I would have thought that the declaration would have
> > sufficed, and that if the compiler then chose some other
> > function, the definition would not have been instantiated.

> Perhaps I'm off-base here, but it looks to me as if the error
> occurs upon the instantiation of the declaration.

Yes. I realized that later.

> Doesn't instantiation of the function declaration require
> instantiating the function's return type?

I think it is unspecified, according to §14.7.1/5. Another
poster, Gene Bushuyev, gave an example of where the compiler
didn't instantiate the return type. (Basically, if there is
only a template class declaration, and not a definition.
Apparently, most compilers will not complain in this case.
Except if they actually need the definition for overload
resolution, hopefully.)

> And isn't that what fails?

> I'd say that the real source of the problem is that
> std::iterator_traits<T> is defined in a manner quite unlike
> the typical traits class template. Most commonly, the
> "default" implementation of a traits class template either
> provides some reasonable default value or type, or is empty.
> In either case, it's possible to instantiate the trait for any
> arbitrary type -- though using it may fail (in the case of the
> empty implemention).

I don't know that there's anything "atypical" or not about
iterator_traits. I've seen all sorts of things in traits
classes. And I don't do any real template meta-programming --
I'd hate to think of what a guy like Andrei puts in them.

> I suspect that the reason for this design decision was to
> allow std::iterator_traits to work with iterator types which
> are member types of containers. (Otherwise, you'd have to
> partially specialize iterator_traits on a member name of a
> template...which is not possible.)

The goal is for iterator_trais to work. Period. The simplest
solution, of course, would just be to require that the iterator
be its own traits class -- that user code could simply write
"Iterator::value_type", and be done with it. Except that for
some reason, it was decided that T* must be a valid iterator as
well. And there is no way to make "T*::value_type" a legal
typename. iterator_traits is just a work-around for that
problem. It takes the type information from the instantiation
type (where it logically belongs) by default, but is specialized
on pointers to generate it from the pointer type.

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

ka...@gabi-soft.fr

unread,
Aug 3, 2005, 10:27:28 AM8/3/05
to
Gianluca Silvestri wrote:
> [snip]
> | IMHO, this is where something should be done. I don't see
> | any reason for the compiler to instantiate the definition of
> | the function unless it is actually chosen by function
> | overload resolution.

> | > And here is finally the *big* question: Is this failure
> | > part of the template deduction machanism or not ? the
> | > compilers tested seems to answer no to this question. I'm
> | > no expert, but to me the answer should be yes.

> | I think that the answer is definitly no. Argument deduction
> | only involves the function declaration, not its definition.
> | So it works. IMHO, the "error" (in the standard) is then
> | requiring (or even allowing) the instantiation of the
> | definition when the function is not chosen by function
> | overload resolution.

> So you agree that there is no deduction failure but a true
> error in template instantiation.

That's what I think today. It seems like every time I
reconsider the question, I come up with a different answer, but
on the whole, today, I think that it is unspecified whether the
code is legal or not, according to §14.7.1/5: "If the overload


resolution process can determine the correct function to call
without instantiating a class template definition, it is
unspecified whether that instantiation actually takes place."

It think that it is very clear that if the class is
instantiated, there will be an error. (Not template argument
deduction failure, but a real error.)

> Bu then you want the error to be ignored because that template
> wouldn't be chosen anyway from overload resolution.

That would be the ideal. In practice, there are two further
constraints:

-- Sometimes, the compiler will need the instantiation in order
to determine the correct function to call. In such cases,
it must instantiate the template. And an error when
instantiating a template is an error; SFINAE does not apply.

-- Depending on compiler implementation techniques, it may be
difficult to determine beforehand whether an instantiation
of the class template is necessary. I know that if I were
writing a compiler, I would like to implement the various
steps in order, e.g. using template argument deduction to
add functions to the overload set without considering what
other members might be in that overload set. And I wouldn't
like carrying a lot of "maybe" information around.

> If it is what you meant I totally agree with you but the
> problem that the Standard should review the overload process
> regarding templates. During overload resolution a template
> might produce an error during istantiation but the compiler
> must keep going on until it finds a best match. It the winner
> of the match is NOT the template that causes the error all
> went wll, otherwise it must issue an error.

> It seems like a proposal for a revision, but I think it's too
> late now.

No. My proposal for the revision was based on a too hasty
reading, supposing that the compiler was instantiating the
*definition* of the function. The problem here is that it is
instantiating the declaration. And that it must instantiate at
least part of the declaration in order to perform overload
resolution.

> | Note that in the non-template case, if you provide several
> | overloaded function declarations, you are only required to
> | provide a definition for those functions which are actually
> | called. Instantiating the template definition provides a
> | definition, and should logically only occur if the
> | definition is needed.

> But here the problem is in the declaration, not the definition

Yes. I missed that in my original answer.

> | Maybe someone who is familiar with the standard's position
> | on templates could clarify the issue: there must be a reason
> | behind what the standard requires.

> Perhaps a thread on comp.std.c++ would be a better place to
> discuss these things

Once we figure out what we are really discussing:-).

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

john...@yahoo.com

unread,
Aug 3, 2005, 6:36:28 PM8/3/05
to
ka...@gabi-soft.fr wrote:

> john...@yahoo.com wrote:
> > Doesn't instantiation of the function declaration require
> > instantiating the function's return type?
>
> I think it is unspecified, according to §14.7.1/5.

Ah, right, so it is. Of course, that means that code that depends upon
either choice is non-portable.


> > I'd say that the real source of the problem is that
> > std::iterator_traits<T> is defined in a manner quite unlike
> > the typical traits class template. Most commonly, the
> > "default" implementation of a traits class template either
> > provides some reasonable default value or type, or is empty.
> > In either case, it's possible to instantiate the trait for any
> > arbitrary type -- though using it may fail (in the case of the
> > empty implemention).
>
> I don't know that there's anything "atypical" or not about
> iterator_traits. I've seen all sorts of things in traits
> classes.

Hmmm...I suppose that's possible. I've always thought of
"empty/default unspecialized case + interesting stuff in the
specializations" as the classic traits pattern, but perhaps I've just
had a sheltered life. :-)

In any event, if that pattern had been followed in the case of
iterator_traits, the problem under discussion in this thread would not
have arisen.


> > I suspect that the reason for this design decision was to
> > allow std::iterator_traits to work with iterator types which
> > are member types of containers. (Otherwise, you'd have to
> > partially specialize iterator_traits on a member name of a
> > template...which is not possible.)
>
> The goal is for iterator_trais to work. Period. The simplest
> solution, of course, would just be to require that the iterator
> be its own traits class -- that user code could simply write
> "Iterator::value_type", and be done with it. Except that for
> some reason, it was decided that T* must be a valid iterator as
> well. And there is no way to make "T*::value_type" a legal
> typename. iterator_traits is just a work-around for that
> problem.

Yes, agreed so far...

> It takes the type information from the instantiation
> type (where it logically belongs) by default, but is specialized
> on pointers to generate it from the pointer type.

...and here we disagree. Once we've decided to go the traits route,
it's no longer clear that the information "logically belongs" inside
the instantiation type. There is at least an equally compelling
argument that it belongs in explicit specializations of the traits
class.

We could have specified iterator_traits as:

template < class T > struct iterator_traits {};

and allowed iterator writers to write:

template < class T> class my_iterator;
namespace std {
template < class T >
struct iterator_traits< my_iterator<T> > :
iterator< forward_iterator_tag, T > {};
}

So the interesting question is, why isn't iterator_traits specified
like this?

One possibility, of course, is that it was simply a matter of saving a
little typing. I hope that's not all there was to it.

A more serious drawback would be that there's no handy way to provide a
partial specialization on a template member type...i.e. I don't know
how you'd handle:

template <class T, class A>
class vector {
// ...
class iterator;
}

// this doesn't work:
template <class T, class A>
struct iterator_traits< vector<T, A>::iterator > {
// ...
};

So, defining the unspecialized iterator_traits as empty would force
container implementors to define container iterators as non-nested
types:

template < class T, class A >
class __vector_iterator {
// ...
};

template < class T, class A >
class vector {
// ...
typedef __vector_iterator< T, A > iterator;
};

...to which some implementors might object. That's just conjecture on
my part, but it's the only real drawback to this approach that
immediately occurs to me.

The advantage, of course, is that it would have prevented the OP's
problem.

ka...@gabi-soft.fr

unread,
Aug 4, 2005, 6:16:44 AM8/4/05
to
john...@yahoo.com wrote:
> ka...@gabi-soft.fr wrote:
> > john...@yahoo.com wrote:
> > > Doesn't instantiation of the function declaration require
> > > instantiating the function's return type?

> > I think it is unspecified, according to §14.7.1/5.

> Ah, right, so it is. Of course, that means that code that
> depends upon either choice is non-portable.

Yup. Note that in the case in question, it's also unspecified
whether std::distance is visible or not -- you're only
guaranteed to get it if you include <iterator>.

[...]


> > > I suspect that the reason for this design decision was to
> > > allow std::iterator_traits to work with iterator types
> > > which are member types of containers. (Otherwise, you'd
> > > have to partially specialize iterator_traits on a member
> > > name of a template...which is not possible.)

> > The goal is for iterator_trais to work. Period. The
> > simplest solution, of course, would just be to require that
> > the iterator be its own traits class -- that user code could
> > simply write "Iterator::value_type", and be done with it.
> > Except that for some reason, it was decided that T* must be
> > a valid iterator as well. And there is no way to make
> > "T*::value_type" a legal typename. iterator_traits is just
> > a work-around for that problem.

> Yes, agreed so far...

> > It takes the type information from the instantiation type
> > (where it logically belongs) by default, but is specialized
> > on pointers to generate it from the pointer type.

> ...and here we disagree. Once we've decided to go the traits
> route, it's no longer clear that the information "logically
> belongs" inside the instantiation type. There is at least an
> equally compelling argument that it belongs in explicit
> specializations of the traits class.

The question is, I suppose, partially about what we mean by
"logically belongs". IMHO, the information logically belongs in
the iterator class, which means that iterator_traits isn't
necessary. Adding an iterator_traits class so that we can use
pointers doesn't really make a difference in that.

Practically, of course, we don't really want to have to write
two classes every time we need a new type of iterator. Having
an iterator_traits which does the right thing automatically
certainly makes life easier, regardless of the logical design
issues.

> We could have specified iterator_traits as:

> template < class T > struct iterator_traits {};

> and allowed iterator writers to write:

> template < class T> class my_iterator;
> namespace std {
> template < class T >
> struct iterator_traits< my_iterator<T> > :
> iterator< forward_iterator_tag, T > {};
> }

> So the interesting question is, why isn't iterator_traits
> specified like this?

Because it means more work for the programmer, for no real
advantage that I can see. Logically, the iterator must know
these types. Having to specify them in any way elsewhere is
duplicate work, and increases the risk of an error. (Suppose I
modify my iterator so that the distance_type is long long. It
would be too easy to forget to also modify the traits
definition.)

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

Gianluca Silvestri

unread,
Aug 4, 2005, 8:29:13 AM8/4/05
to

<ka...@gabi-soft.fr> ha scritto nel messaggio
news:1123074160.0...@g49g2000cwa.googlegroups.com...

|
| > Perhaps a thread on comp.std.c++ would be a better place to
| > discuss these things
|
| Once we figure out what we are really discussing:-).

To me the point is:
given this function template
template<class T>
typename std::iterator_traits<T>::difference_type distance(T, T)
{
//...
}

should an instantiation failure of this template in an overload resolution
context (when there are other function[s] [templates] with the ame name)
make the program ill-formed, or should the function template simply be
removed from the overload set?


---
Gianluca Silvestri


Gabriel Dos Reis

unread,
Aug 4, 2005, 8:35:31 AM8/4/05
to
ka...@gabi-soft.fr writes:

[...]

| The goal is for iterator_trais to work. Period. The simplest
| solution, of course, would just be to require that the iterator
| be its own traits class -- that user code could simply write
| "Iterator::value_type", and be done with it. Except that for
| some reason, it was decided that T* must be a valid iterator as
| well. And there is no way to make "T*::value_type" a legal
| typename.

Sure, there is: The committee can decree that "T*" is a type-name --
it certainly is a *type-id* in current C++. But, I uess the real
issue is whether one wanted to invent scopes for things like "T*" or
"T&". Well, I believe the time has come.

--
Gabriel Dos Reis
g...@integrable-solutions.net

Khoguan Phuann

unread,
Aug 4, 2005, 9:29:32 AM8/4/05
to

Hi, I am the original poster. Thanks a lot for your continual
discussion of my question.

James, the second sentence of the above quote "If a substitution


in a template parameter or in the function type of the function

template results in an invalid type, type deduction fails." is
from the third bullet of 14.8.2/2, right? But 14.8.2/2 says that
"Specifically, the following steps are performed when evaluating
an explicitly specified template argument list with respect to a
given function template:". And the quote is one of the steps.
So it looks like the steps(rules) only apply to a call of a
function template with explicitly specified template argument
list, and my original problematic distance() call should have
been called as:

distance<std::vector<int> >(v1, v2);

for the 14.8.2/2 rules to apply, otherwise they won't apply?
BTW, do the steps in 14.8.2/2 serve as the formal, core
definition of SFINAE, an informal term coined by Vandevoorde?

-- Khoguan Phuann

Khoguan Phuann

unread,
Aug 4, 2005, 1:04:09 PM8/4/05
to
ka...@gabi-soft.fr wrote:

> Gianluca Silvestri wrote:
> > Bu then you want the error to be ignored because that template
> > wouldn't be chosen anyway from overload resolution.
>
> That would be the ideal. In practice, there are two further
> constraints:
>
> -- Sometimes, the compiler will need the instantiation in order
> to determine the correct function to call. In such cases,
> it must instantiate the template. And an error when
> instantiating a template is an error; SFINAE does not apply.
>
> -- Depending on compiler implementation techniques, it may be
> difficult to determine beforehand whether an instantiation
> of the class template is necessary. I know that if I were
> writing a compiler, I would like to implement the various
> steps in order, e.g. using template argument deduction to
> add functions to the overload set without considering what
> other members might be in that overload set. And I wouldn't
> like carrying a lot of "maybe" information around.

So it's a more straightforward way for the compiler writers to
just instantiate iterator_traits<std::vector<int> > and the
constraints you said seem to serve to explain why GCC, VC, and
Comeau all behave the same by issuing an error, while the
standard leaves it as unspecified.

> > | Maybe someone who is familiar with the standard's position
> > | on templates could clarify the issue: there must be a reason
> > | behind what the standard requires.
>
> > Perhaps a thread on comp.std.c++ would be a better place to
> > discuss these things
>
> Once we figure out what we are really discussing:-).

I found out that core issue #415 "Template deduction does not
cause instantiation", with "drafting" status and submitted on 4
May 2003, looks like to be directly related. But I can not fully
follow the committee members' discussion therein.

--Khoguan Phuann

Daniel Krügler

unread,
Aug 4, 2005, 1:02:18 PM8/4/05
to
Hello James Kanze,

ka...@gabi-soft.fr wrote:
> I know that there is some discussion in the standards committee
> concerning concepts. I wonder if that might help here: from
> what little I know, std::distance would require that
> InputIterator conform to the InputIterator concept, and of
> course, vector<int> will not conform to that concept. But
> whether the error is considered to occur in argument deduction,
> or at a later time, I don't know.

Recently I had an open discussion in c.s.c++ with Douglas Gregor
concerning the most recent state of concept paper N1799:

http://www2.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1799.pdf

(Look for the thread "Some questions concerning concept proposal N1799")

According to this proposal (nad the discussion) concept-where clauses
take part in overload resolution. Thus functions with mismatching
concepts will not be selected for the overload set.

Greetings from Bremen,

Daniel Krügler

Allan W

unread,
Aug 4, 2005, 5:24:50 PM8/4/05
to
> Allan W wrote:
> > I assume this is not a FAQ? Should it be? Or would it just
> > confuse beginners?

ka...@gabi-soft.fr wrote:
> Some things are best left unsaid:-). I would be very, very
> sceptical of any program which really used this.
>
> You'll have to admit that its an amusing bit of trivia, however.

Yes, I admit it.

"You learn something new every day." But usually not something
this surprising or interesting.

ka...@gabi-soft.fr

unread,
Aug 5, 2005, 11:10:32 AM8/5/05
to

Not too discouraging, I hope:-).

> James, the second sentence of the above quote "If a
> substitution in a template parameter or in the function type
> of the function template results in an invalid type, type
> deduction fails." is from the third bullet of 14.8.2/2, right?
> But 14.8.2/2 says that "Specifically, the following steps are
> performed when evaluating an explicitly specified template
> argument list with respect to a given function template:". And
> the quote is one of the steps. So it looks like the
> steps(rules) only apply to a call of a function template with
> explicitly specified template argument list,

That's what I thought at first. However, in §14.8.2/4, it says

The resulting substituted and adjusted function type is used
as the type of the function template for template argument
deduction. When all template arguments have been deduced,


all uses of template parameters in nondeduced contexts are
replaced with the corresponding deduced argument values. If

the substitution results in an invalide type, as described
above, type deduction fails.

I find the first sentence particularly obscure, but my
interpretation of this paragraph is that the compiler does the
following steps:

1. It substitutes any explicitly given template arguments,
doing the checks listed. If the checks result in an error,
type deduction fails, and the function is not considered
further. Otherwise,

2. The compiler substitutes the explicit arguments, creating
(more or less) a new template, with only the parameters not
given explicitly as parameters. (My interpretation of the
first sentence above.)

3. It does the type deduction described on §14.8.2.1 on this
"new" template function.

4. It then processes these deduced types as it originally
processed the explicitly given arguments. This is the "When
all template arguments have been deduced [...]" part above.

But as I say, that's really just my best guess as to what is
meant.

> and my original problematic distance() call should have been
> called as:

> distance<std::vector<int> >(v1, v2);

> for the 14.8.2/2 rules to apply, otherwise they won't apply?

I think the last two sentences in §14.8.2/4 mean that they also
apply here. Basically, since there are no explicit template
arguments, the type of the (template) function used in type
deduction is precisely the declared type. After than, the
compiler does the deduction (which is pretty easy in this case,
and results in std::vector<int> for the single type parameter).
Then, supposing my interpretation of the last two sentences of
§14.8.2/4 is correct, it does the substitution checks in exactly
the same manner as if the deduced arguments had been specified
explicitly.

> BTW, do the steps in 14.8.2/2 serve as the formal, core
> definition of SFINAE, an informal term coined by Vandevoorde?

That's my understanding. If I recall correctly, the "SF" is
"substitution failure"; substitution failure is certainly what
these steps are talking about.

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

ka...@gabi-soft.fr

unread,
Aug 5, 2005, 11:12:35 AM8/5/05
to
Gianluca Silvestri wrote:
> <ka...@gabi-soft.fr> ha scritto nel messaggio
> news:1123074160.0...@g49g2000cwa.googlegroups.com...

> | > Perhaps a thread on comp.std.c++ would be a better place
> | > to discuss these things

> | Once we figure out what we are really discussing:-).

> To me the point is:
> given this function template
> template<class T>
> typename std::iterator_traits<T>::difference_type distance(T, T)
> {
> //...
> }

> should an instantiation failure of this template in an
> overload resolution context (when there are other function[s]
> [templates] with the ame name) make the program ill-formed, or
> should the function template simply be removed from the
> overload set?

I don't think that there is any question about that. An error
is an error, and if instantiating a class results in an error,
and the compiler is require to or allowed to instantiate the
class, then the code contains an error. The only time that an
error might simply cause the a function template to be removed
for the overloading set is if it is the immediate result of
substituting a deduced parameter in a function template. That's
not the case here. In this case, substituting the deduced
parameters results in a case where it is unspecified whether the
compiler must instantiate a class template or not; if the
compiler does instantiate it, it is not a substitution error,
but and error in instantiating a class. Which is an error, full
stop.

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

ka...@gabi-soft.fr

unread,
Aug 5, 2005, 11:13:25 AM8/5/05
to

More or less. It's what John Spicer said in his comments on the
core issue you cite: instantiating one template may trigger
instantiating others, etc., and it isn't reasonable to require
the compiler to maintain sets of "speculative" instantiations;
instantiations made on the grounds that they might be needed,
but in which an error cannot be treated as an error, because it
won't be one if the instantiation is needed.

Why it's unspecified, and not an error, I don't know. Probably
to allow compilers to skip the instantiation if an
implementation isn't available (generating an error because of
this if they later need the instantiation).

> > > | Maybe someone who is familiar with the standard's
> > > | position on templates could clarify the issue: there
> > > | must be a reason behind what the standard requires.

> > > Perhaps a thread on comp.std.c++ would be a better place
> > > to discuss these things

> > Once we figure out what we are really discussing:-).

> I found out that core issue #415 "Template deduction does not
> cause instantiation", with "drafting" status and submitted on
> 4 May 2003, looks like to be directly related. But I can not
> fully follow the committee members' discussion therein.

There's not much to follow, at least in the public section. But
it certainly does concern the case at hand -- it is, in fact,
exactly the same example. The only thing they changed was to
extract the relevant parts of the standard functions and
classes, and put them into global namespace, in order to
abstract out the issues of ADL.

I'm not sure I totally agree with John Spicer's last comment (or
rather, I don't fully understand it). Basically, at this point,
when talking about "intantiating a template" it is important to
distinguish whether we are talking about instantiating the
function definition, or the function declaration. I don't
really know the background here, but I suspect that the reason
for the global replacement John mentions is that the standard
doesn't ever define what "generate a single template function"
means. I suspect that the original intent was that it means
roughly the same thing as "instantiate the template function
declaration", but not the definition. But as John Spicer was
the orginal author of this part (I think), he would know better
than I.

Beyond that, I would much prefer that the standard specify one
way or the other concerning whether the class template is
instantiated, so we know whether the code is legal or not.
(Because it is so hard to specify exactly when the instantiation
might actually be needed, I rather imagine that if it were
specified, the standard would require the instantiation.)

Given that, I think it would be useful to raise the question in
comp.std.c++. As I am about to leave on holiday, however, and
will be two weeks without any access to a computer at all, I'm
not really in a position to do so (although the question would
interest 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

Paul Mensonides

unread,
Aug 6, 2005, 7:15:19 AM8/6/05
to
Gianluca Silvestri wrote:

> All the problem is originated by the the fact that:
> 1. standard doesn't mandate the dependencies between header files, so
> the OP's library implementation, when including <vector>, also
> includes <iterator>
> 2. the declaration of std::distance in <iterator> is:
> template<class InputIterator>
> typename iterator_traits<InputIterator>::difference_type
> distance(InputIterator first, InputIterator last);
>
> So the compiler, once found the templated distance(), tries to deduce
> the template parameters to see if it can add the an instantiation of
> the function to the overload set. In doing so it succeeds in the
> substitution of InputIterator with std::vector<int>. Then it tries to
> instantiate std::iterator_traits<vector<int> >, and it find the
> primary template definitionl. Where it fails is when it looks for the
> member std::vector<int>::difference_type

Not this one...

> defined as typedef in
> std::iterator_traits<std::vector<int> >. And here is finally the *big*
> question:
> Is this failure part of the template deduction machanism or not ? the
> compilers tested seems to answer no to this question. I'm no expert,
> but to me the answer should be yes.

Here is what happens (in a reduced example):

#include <iostream>

template<class T> struct trait {
typedef typename T::type type;
};

namespace A {

struct X {
typedef int type;
};

template<class T> typename trait<T>::type f(T) {
return 0;
}

}

int f(A::X) {
return 1;
}

int main(void) {
std::cout << f(A::X()) << '\n';
return 0;
}

The overload selected is the global one, and the output of the program is 1.
However, if you comment out the typedef in X, you get the same kind of error.
In other words, in this (and the original) example overload resolution is doing
the right thing. The problem occurs before overload resolution--during argument
deduction on the template function found through ADL prior to it being added to
the candidate set. SFINAE doesn't apply here, because trait<T> does have a
nested ::type, it is just that X (with the typedef commented) does not. ::type
is being used outside of the declaration (where SFINAE applies) in traits<T>
(where SFINAE doesn't apply). I.e. SFINAE applies here...

template<class T> typename trait<T>::type f(T);
^^^^^^
but does not apply here...

template<class T> struct trait {
typedef typename T::type type;
^^^^^^
};

Now, the instantiation of trait<T> is a full instantiation that includes the
instantiation of its member typedefs. In the original example, the failure
isn't because vector doesn't have ::difference_type (it does); the failure is
because vector doesn't have ::iterator_category.

Note that if trait was defined differently, such as

template<class T> struct trait : T { };

Then the access would be in the function declaration, causing SFINAE to apply,
and silently removing the function as a candidate.

If the compiler was required to apply SFINAE to arbitrary depths of template
instantiations, then the compiler would have to be able to roll back all those
instantiations when a type substitution failed. Vendors have been complaining
about this being extremely difficult for some time now, and it is the primary
reason (AFAICT) that SFINAE does not apply to expressions. Personally, I think
that vendors should bite the bullet, because C++ would be a better language if
it did this. It leads to surprising (and very indirect) errors because it does
not. Unfortunately, that is the way it is right now.

Regards,
Paul Mensonides

Paul Mensonides

unread,
Aug 6, 2005, 7:22:15 AM8/6/05
to

Alf P. Steinbach

unread,
Aug 9, 2005, 11:43:54 AM8/9/05
to
* Alf P. Steinbach, referring to §3.4.2/2:
> With my current understanding I think this -- that ordinary lookup
> doesn't have priority in general, only for class member functions --
> should be corrected, because when only the compiler can figure out
> what's going on for a simple function call, the language rules are just
> plain wrong.

This thread has brought additional understanding about the details, but
AFAICS the problem sketched above is still there.

In addition, "a eriksson" <aeri...@gmail.com> recently posted a message in
[comp.lang.c++] titled "Why ambiguous base when one is inherited private?",
with example, here trimmed to essentials for this thread,

class A {};

class B : private A {};

class C : public B, public A {};

void foo(A& a) {}

int main()
{
C c;
foo(c); // error: `A' is an ambiguous base of `C'
}

and this -- that overload resolution happens before access checking --
is also counter-intuitive, picking up a definition one would think would be
well out of harm's way; one would think that what one does in private isn't
"known" to the rest of the system (I guess wrt. virtual functions, a third
such case, there's no way around it, because we want those two concepts to
be orthogonal, but the above doesn't seem to be _useful_ in any way).


> However,it may be that someone explains why that restriction to class
> member functions was put in there, and that the explanation makes good
> sense.

No such explanation of the restriction has been given. An explanation of
why such priority for class members is desirable has been given. But that
explanation -- essentially that priority is what one expects -- seems to
equally apply to definitions earlier in the current or enclosing scopes.


> In that case my fallback opinion (;-)) is that _something_ should be
> corrected.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Bo Persson

unread,
Aug 10, 2005, 5:52:40 AM8/10/05
to

"Alf P. Steinbach" <al...@start.no> skrev i meddelandet
news:42f8b999....@news.individual.net...

>* Alf P. Steinbach, referring to §3.4.2/2:
>> With my current understanding I think this -- that ordinary
>> lookup
>> doesn't have priority in general, only for class member

What about this example:

class A
{
public:
void f(float);
private:
void f(int);
};

class B : public A
{
public:
void g()
{ f(42); }
};


Would you expect g() to call f(float) as the only candidate, or the
compiler to tell you that f(int) isn't accessible from class B?

Bo Persson

Alf P. Steinbach

unread,
Aug 10, 2005, 2:57:25 PM8/10/05
to
* Bo Persson gives Yet Another Example:

>
> class A
> {
> public:
> void f(float);
> private:
> void f(int);
> };
>
> class B : public A
> {
> public:
> void g()
> { f(42); }
> };
>
>
> Would you expect g() to call f(float) as the only candidate, or the
> compiler to tell you that f(int) isn't accessible from class B?

Depends on how much one knows of and has become accustomed to idiosyncratic
C++ rules... I'd certainly _prefer_ f(float) as the only candidate. In an
auto listing of the class' protected interface I'd never even see f(int), so
the compiler would be (and is ;-)) considering things inaccessible to me.

One question is whether it ever can be practically useful to have the
compiler consider inaccessible things in name resolution.

The only affirmative answer I can think of is that it might be more
efficient for the compiler, but then that can go both ways: it might be more
efficient to consider smaller sets, and I think that is far more probable.

That seems to indicate that a change would both improve the conceptual
consistency and (probably) the efficiency of compilation.

Another question is whether a change, keeping the existing treatment of
virtual function overrides and the C style pointer cast to base class as
exceptions, could break existing code. The examples so far show now invalid
code that would then compile fine. Is there an example of code that now
compiles that would then become invalid or have its meaning changed?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Falk Tannhäuser

unread,
Aug 11, 2005, 6:24:13 AM8/11/05
to
Alf P. Steinbach wrote:
> * Bo Persson gives Yet Another Example:
>>class A
>>{
>>public:
>> void f(float);
>>private:
>> void f(int);
>>};
>>
>>class B : public A
>>{
>>public:
>> void g()
>> { f(42); }
>>};
>>
>>
>>Would you expect g() to call f(float) as the only candidate, or the
>>compiler to tell you that f(int) isn't accessible from class B?
>
> Depends on how much one knows of and has become accustomed to idiosyncratic
> C++ rules... I'd certainly _prefer_ f(float) as the only candidate. In an
> auto listing of the class' protected interface I'd never even see f(int), so
> the compiler would be (and is ;-)) considering things inaccessible to me.
>
> One question is whether it ever can be practically useful to have the
> compiler consider inaccessible things in name resolution.

I think the rationale of the current C++ rule is that changing the
protection level of class members shall NEVER change the semantics
of a program. So if you weaken the protection level (from private
to protected, protected to public or private to public) in a well-formed
program, it continues to be well-formed an to have the same semantics;
while if you strengthen the protection level, the program may not
longer compile due to class members becoming inaccessible - but if it
still does compile, the semantics will not change either.

The problem presented above looks a bit academical to me - in practice,
in a well-designed class (and we know that in practice all our classes
are well-designed, don't we?) you don't give the same name to several
member functions unless they do The Same Thing (tm), and in this case,
why should the have different accessibilities? The only possible
exception to this rule I can think off are constructors...

Falk

Vladimir Marko

unread,
Aug 11, 2005, 6:23:44 AM8/11/05
to
Alf P. Steinbach wrote:
> One question is whether it ever can be practically useful to have the
> compiler consider inaccessible things in name resolution.
>
> The only affirmative answer I can think of is that it might be more
> efficient for the compiler, but then that can go both ways: it might be more
> efficient to consider smaller sets, and I think that is far more probable.
>
> That seems to indicate that a change would both improve the conceptual
> consistency and (probably) the efficiency of compilation.

Improve conceptual consistency?

class X{
public:
void foo(double) { }
private:
void foo(int) { }

friend void bar_f();
};

void bar() { X().foo(1); }
void bar_f() { X().foo(1); }

The friendly bar_f chooses the private foo while the non-friendly bar
chooses the public one. Do you call that consistent?

If two functions with identical bodies compile successfuly they should
do the same. It's much better if one does not compile than if they had
different meanings. So let's stick to the old rules with a simple
guideline: never give two overloads a different access level if there
is a reasonable set of arguments for which both overloads are a match
(malicious arguments that convert to anything do not count).

Best Regards,
Vladimir Marko

0 new messages