Why won't this template function compile?

Hi,
Here's the minimal program illustrating the "mystery":
#include <vector>
using namespace std;
template <typename T>
T silly_f (typename vector<T>::const_iterator begin,
typename vector<T>::const_iterator end)
{
return *begin;
}
int main()
{
vector<int> values;
silly_f (values.begin(), values.end());
return 0;
}
With g++ 4.1.1, I get an error in the line that calls the function ---
it tells
me "no match for call to silly_f ( ..... iterator .... , .....
iterator ...) --- g++
3.3.3 also gives me the same error
(the .... refers to the expanded gibberish for vector<int>::iterator )
I know that the "better" solution is to do:
template <typename Iterator>
typename Iterator::value_type silly_f (Iterator begin, Iterator end)
(not only it is better and more elegant --- it also compiles)
But I'm puzzled and would like to find out why won't the other
version compile.
Thanks,
Carlos
--
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

> > Here's the minimal program illustrating the "mystery":
> [...]
> > template <typename T>
> > T silly_f (typename vector<T>::const_iterator begin,
> > typename vector<T>::const_iterator end)
> [...]
> > int main()
> > {
> > vector<int> values;
> > silly_f (values.begin(), values.end());
> [...]
>
> vector<int> values;
> silly_f <int> (values.begin(), values.end());
Still does not compile (same error message!!) --- which makes it
even more puzzling. I thought it would compile, and I was still
going to ask: "but why won't the other one compile?" That is,
my interest is not really in making it work --- there is the better
solution anyway, doing template <typename Iterator> --- but
rather in understanding why is it that the compiler won't deduce
and won't see a match, even if we deduce it for the compiler?
Anyone?
Thanks,
Carlos
--
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

> > template <typename T>
> > T silly_f (typename vector<T>::const_iterator begin,
> > typename vector<T>::const_iterator end)
> values.begin() is of type vector<int>::iterator, not of type
> vector<int>::const_iterator. Thus the compiler can't match the type
> of the first argument and can't deduce the template argument.
D'oh! I would have sworn that I copy-n-pasted, but apparently I
didn't,
since in my code, I used iterator, and not const_iterator" ...
(sorry
about that! I shouldn't make these types of trivial mistakes!)
Ok, now I'm going to copy-n-paste the entire file, and post the
*exact*
error message:
--- BEGIN FILE test.c++ ---
#include <vector>
using namespace std;
template <typename T>
T silly_f (typename vector<T>::iterator begin,
typename vector<T>::iterator end)
{
return *begin;
}
int main()
{
vector<int> values;
silly_f (values.begin(), values.end());
return 0;
}
--- END FILE test.c++ ---
Compiled with: c++ -c test.c++
test.c++: In function 'int main()':
test.c++:14: error: no matching function for call to
'silly_f(__gnu_cxx::__normal_iterator<int*, std::vector<int,
std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*,
std::vector<int, std::allocator<int> > >)'
Now, the funny thing is: with the previous poster's suggestion
(calling it
like silly_f<int> ( ... ) ), then it does compile (I was on another
computer
and I copy-n-pasted from what I had posted --- so now, the error was
due
to the const_iterator vs. iterator).
So, the mystery is still on ... (compiler bug? I still refuse to
believe that
this may be the case --- g++ is notoriously good at template stuff;
this
one seems like a particularly trivial bug when it comes to templates,
so
it would be quite surprising)
Is it possible that it is related to the nested-type-in-a-template
issue?
The same reason why we need to "help" the compiler, using the keyword
typename so that it knows that vector<T>::iterator refers to a type?
Even if that's the case, I don't know ... Seems like a trivial match
to
deduce...
Thanks,
Carlos
--
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

0

Carlos

4/18/2007 11:44:20 AM

> > > silly_f <int> (values.begin(), values.end());
>
> > Still does not compile (same error message!!)
>
> This does compile with gcc 3.4.4 and 4.1.2.
Silly error from my part --- see my other post in this sub-thread.
> > "but why won't the other one compile?"
>
> Because the standard says so in 14.8.2.4/4.
> -4- The nondeduced contexts are:
> * The nested-name-specifier of a type that was
> specified using a qualified-id.
Hmmm, is this really the case?? I'm probably confused about the
terminology, but the nested-name is iterator, and not T (iterator
was qualified with the scope-resolution operator).
> The compiler is forbidden to try and deduce the type T. But, even
> if it wasn't, how could it, without the explicit knowledge of
> std::vector,
> tell that T=int is a match? And more, how could it tell that it's the
> _only_ match?
And who said that the compiler has no explicit knowledge of
std::vector?? I #included <vector>, where the compiler finds
everything that it ever may need to know about vector (not to
mention the issue that, since vector is fully documented in the
standard, why wouldn't the compiler assume knowledge about
it? --- well, ok, this is an entirely separate discussion, so let's
say that we avoid that tangent)
The thing is, putting aside the fact that the standard says that
there is no match --- I don't see why it could not see that match
(seems obvious enough for you; maybe it's related to the way
compilers are implemented? An area which I'm 150% unfamiliar
with!) ...
The given type is known --- vector<int>::iterator; an available
template has been given, and fully described/visible to the
compiler, since #include <vector> is present; the available
template has a type member iterator; this constitutes a match
for vector<int>::iterator given by vector<T>::iterator for T=int.
Why would the compiler not know, or not know that it is the
only match?
Carlos
--
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

0

Carlos

4/18/2007 1:46:36 PM

Carlos Moreno wrote:
::
:: And who said that the compiler has no explicit knowledge of
:: std::vector?? I #included <vector>, where the compiler finds
:: everything that it ever may need to know about vector (not to
:: mention the issue that, since vector is fully documented in the
:: standard, why wouldn't the compiler assume knowledge about
:: it? --- well, ok, this is an entirely separate discussion, so let's
:: say that we avoid that tangent)
::
:: The thing is, putting aside the fact that the standard says that
:: there is no match --- I don't see why it could not see that match
:: (seems obvious enough for you; maybe it's related to the way
:: compilers are implemented? An area which I'm 150% unfamiliar
:: with!) ...
::
:: The given type is known --- vector<int>::iterator; an available
:: template has been given, and fully described/visible to the
:: compiler, since #include <vector> is present; the available
:: template has a type member iterator; this constitutes a match
:: for vector<int>::iterator given by vector<T>::iterator for T=int.
::
:: Why would the compiler not know, or not know that it is the
:: only match?
The problem is that the compiler cannot know that vector<int>::iterator is
different from all other vector<T>::iterator, until it has tried to
instantiate std::vector for ALL possible values of T.
What if I have:
struct MySillyType
{ };
template<>
class std::vector<MySillyType>
{
typedef std::vector<int>::iterator iterator;
};
Now silly_f<int>() and silly_f<MySillyType>() has the same signature.
How is
the compiler to know? It just cannot!
Bo Persson
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

0

Bo

4/18/2007 11:09:45 PM

Carlos Moreno schrieb:
> > > template <typename T>
> > > T silly_f (typename vector<T>::const_iterator begin,
> > > typename vector<T>::const_iterator end)
>
> > values.begin() is of type vector<int>::iterator, not of type
> > vector<int>::const_iterator. Thus the compiler can't match the type
> > of the first argument and can't deduce the template argument.
>
> D'oh! I would have sworn that I copy-n-pasted, but apparently I
> didn't,
> since in my code, I used iterator, and not const_iterator" ...
> (sorry
> about that! I shouldn't make these types of trivial mistakes!)
Note that your problem has *nothing* to do with choosing
vector<T>::iterator over vector<T>::const_iterator (that is only
relevant for your internal logic).
Vladimir Marko has given the correct answer: Your are defining
your function template in a way that all function parameters are in
a nondeduced context. In this situation 14.8.2.4/3 applies:
"[..] If a template parameter is used only in nondeduced contexts
and is not explicitly specified, template argument deduction fails."
So in your situation you *must* explicitly provide the argument
type. After doing this the code will compile with either
vector<T>::iterator or vector<T>::const_iterator, because
the vector<T>::iterator is valid and convertible to
vector<T>::const_iterator.
> Now, the funny thing is: with the previous poster's suggestion
> (calling it
> like silly_f<int> ( ... ) ), then it does compile (I was on another
> computer
> and I copy-n-pasted from what I had posted --- so now, the error was
> due
> to the const_iterator vs. iterator).
As explained above in this situation explicitely specifying
the template argument type is required and not optional.
> The same reason why we need to "help" the compiler, using the keyword
> typename so that it knows that vector<T>::iterator refers to a type?
> Even if that's the case, I don't know ... Seems like a trivial match
> to
> deduce...
It has no relation to provide a "typename" in front of a dependent
qualified-id. The reason is that it is in general impossible to deduce
the "base" (or parts of it) from a qualified-id of the actually
qualified
"leaf". Assume that std::vector<int>::iterator is int*. Given the int*
as
result, how do you unambigiously deduce the leading std::vector<int>?
Greetings from Bremen,
Daniel Kr�gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

0

iso

4/18/2007 11:10:17 PM

Carlos Moreno wrote:
> Even if that's the case, I don't know ... Seems like a trivial match
> to deduce...
As has already been posted, T is not deducible from the
arguments. You can read the standard for chapter and verse,
but the basic problem is that there can be an infinite
number of matches. Here's why - I can write a specialization
like this:
class a { };
template <>
class std::vector<a> {
public:
typedef vector<int>::iterator iterator;
};
This makes vector<int>::iterator and vector<a>::iterator be
the same type, so you can see why simply having the iterator
doesn't let you go back to the type parameter.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

> The problem is that the compiler cannot know that vector<int>::iterator is
> different from all other vector<T>::iterator, until it has tried to
> instantiate std::vector for ALL possible values of T.
Ok --- this is the key element that I was failing to see!! (looked
like a
trivial match because I was looking just at a sub-set of the
scenario!)
Still --- and please, do not take the following as me being dense;
just
enjoying the discussion just for the sake of working the ideas:
> struct MySillyType
> { };
>
> template<>
> class std::vector<MySillyType>
> {
> typedef std::vector<int>::iterator iterator;
>
> };
>
> Now silly_f<int>() and silly_f<MySillyType>() has the same signature.
> How is
> the compiler to know? It just cannot!
What do you mean it can not?? We can --- and I mean, our brains do
determine in an *fully objective* and completely unambiguous and
undebatable way, if there is a match or there is ambiguity. Why
wouldn't the compiler be able to?
I mean, the issue is: if two instances of vector<T>::iterator are
the
same, then the call would be ambiguous --- nothing new there: but
if the compiler only sees the one definition (by means of a *generic*
without any specialized definition) that could match, then where's
the problem?
If two iterators for two different instances of vector<T> are the
same,
the compiler should see it --- if you are in a context where the
vector<MySillyType> counts, then the definition of SillyType and
that of vector<SillyType> will have been seen. If they have been
seen, then the compiler will (rightfully) declare ambiguity.
And if they (SillyType and vector<SillyType> have not been seen, then
the match for the call that the compiler is seeing (in the main
program)
can not be that for SillyType, nor it would matter that the compiler
did
not consider SillyType as a candidate match.
What am I missing?
(no, I'm not missing the fact that the standard simply says that the
compiler won't do it --- that part is already understood and
accepted;
my argument now goes around the rationale about why is it not
possible
for the compiler to deduce the template parameter in these types of
situations --- simply curious/puzzled, not trying to be dense)
Thanks,
Carlos
--
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

0

Carlos

4/19/2007 1:43:15 PM

Carlos Moreno wrote:
> We can ... if there is a match or there is ambiguity.
> Why wouldn't the compiler be able to?
> What am I missing?
You're failing to generalize from the examples given.
Perhaps this will make it more clear:
template <typename T> struct X { typedef char (&t)[sizeof(T)]; };
template <typename U> void f(typename X<U>::t);
void g(X<int>::t a) { f(a); }
Can you see why the compiler cannot deduce U from f's argument?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

0

Hyman

4/19/2007 3:40:59 PM

Carlos Moreno wrote:
>> struct MySillyType
>> { };
>>
>> template<>
>> class std::vector<MySillyType>
>> {
>> typedef std::vector<int>::iterator iterator;
>>
>> };
>>
>> Now silly_f<int>() and silly_f<MySillyType>() has the same signature.
>> How is
>> the compiler to know? It just cannot!
>
> What do you mean it can not?? We can --- and I mean, our brains do
> determine in an *fully objective* and completely unambiguous and
> undebatable way, if there is a match or there is ambiguity. Why
> wouldn't the compiler be able to?
Not really, we can tell there is ambiguity only beacause the example is
so trivial. In general, however, it is algorithmically impossible to
decide whether there is an ambiguity or not. I spent the last few hours
learning Boost.Mpl so I could prove it :-) --- in the following code,
'post_checker<I, S>::type' is 'true_' if and only if 'S' is a solution
of the Post correspondence problem 'I' or 'S' is 'true_'. Hence, the
deduction in call to 'foo(true_())' would be ambiguous iff the problem
'instance' had a solution.
Since it is impossible to decide whether a given Post correspondence
problem has a solution, the compiler can say nothing about the ambiguity
of type deduction in call to 'foo(true_())'.
#include <boost/mpl/vector.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/insert_range.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/at.hpp>
using namespace boost::mpl;
template <class Instance, class Solution>
struct post_checker
{
typedef
typename reverse_fold<
Solution,
vector<>,
insert_range<_1, begin<_1>,
at<typename first<Instance>::type, _2> >
>::type left_word;
typedef
typename reverse_fold<
Solution,
vector<>,
insert_range<_1, begin<_1>,
at<typename second<Instance>::type, _2> >
>::type right_word;
typedef typename equal<left_word, right_word>::type type;
};
template <typename Instance>
struct post_checker<Instance, true_>
{
typedef true_ type;
};
// This is an instance of the Post correspondence problem,
// the sequences are ("aba", "bbb", "aab", "bb") and
// ("a", "aaa", "abab", "babba"). For the record,
// the instance has a solution and that is (0, 3, 2, 0)
// if we're indexing from zero. The example was taken
// from Wikipedia page on PCP.
typedef
pair<
vector<
vector_c<char, 'a', 'b', 'a'>::type,
vector_c<char, 'b', 'b', 'b'>::type,
vector_c<char, 'a', 'a', 'b'>::type,
vector_c<char, 'b', 'b'>::type
>,
vector<
vector_c<char, 'a'>::type,
vector_c<char, 'a', 'a', 'a'>::type,
vector_c<char, 'a', 'b', 'a', 'b'>::type,
vector_c<char, 'b', 'a', 'b', 'b', 'a'>::type
>
>
instance;
template <typename Solution>
void foo(typename post_checker<instance, Solution>::type)
{
}
int main()
{
// If the standard allowed the deduction,
// would it be ambiguous in the call below?
//
// foo(true_());
// Test the correctness of post_checker...
foo<true_>(true_());
foo<vector_c<int, 0, 3, 2, 0> >(true_());
foo<vector_c<int, 0, 3, 2, 1> >(false_());
}
--
Best regards,
Martin Vejn�r
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]