Topic: Template deduction fails, except when it succeeds


Author: Victor Bazarov <v.Abazarov@comAcast.net>
Date: Fri, 17 Feb 2006 11:07:17 CST
Raw View
Maciej Sobczak wrote:
> Consider:
>
> template<class E>
> void foo(const E (&tab)[5]) {}
>
> template<class E>
> void bar(const E *) {}
>
> int main()
> {
>   char tab[5];
>   foo(tab);   // 1
>   bar(tab);   // 2
> }
>
> 1. fails, 2. is OK.
>
> When I try to explain 1, my reasoning breaks down because of 2.
> And the other way round.
>
> What exactly makes one fail and the other succeed? What are the allowed
> conversions in which context?
> Please refer to standard paragraphs for justification.

14.2.8.4/9

"T*" is there, "T(&)[]" is not.

V
--
Please remove capital As from my address when replying by mail

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: matti.rintala@tut.fi (Matti Rintala)
Date: Fri, 17 Feb 2006 17:06:30 GMT
Raw View
Maciej Sobczak wrote:
> template<class E>
> void foo(const E (&tab)[5]) {}
>=20
> template<class E>
> void bar(const E *) {}
>=20
> int main()
> {
>   char tab[5];
>   foo(tab);   // 1
>   bar(tab);   // 2
> }
>=20
> 1. fails, 2. is OK.
>
> What exactly makes one fail and the other succeed? What are the allowed
> conversions in which context?
> Please refer to standard paragraphs for justification.

14.8.2.1 1-3:
"Template argument deduction is done by comparing each function template
parameter type (call it P) with the type of the corresponding argument of
the call (call it A) as described below.

If P is not a reference type:
=97 If A is an array type, the pointer type produced by the array-to-poin=
ter
standard conversion (4.2) is used in place of A for type deduction; other=
wise,
=97 If A is a function type, the pointer type produced by the
function-to-pointer standard conversion (4.3) is used in place of A for
type deduction; otherwise,
=97 If A is a cv-qualified type, the top level cv-qualifiers of A=92s typ=
e are
ignored for type deduction."

The first bullet covers your case. Array-to-pointer conversion is used
before template argument deduction.

--=20
------------- Matti Rintala ------------ matti.rintala@tut.fi -----------=
-
Painting is the art of inclusion. Photography is an art of exclusion.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Alf P. Steinbach" <alfps@start.no>
Date: Fri, 17 Feb 2006 14:39:53 CST
Raw View
* Matti Rintala:
> Maciej Sobczak wrote:
>> template<class E>
>> void foo(const E (&tab)[5]) {}
>>
>> template<class E>
>> void bar(const E *) {}
>>
>> int main()
>> {
>>   char tab[5];
>>   foo(tab);   // 1
>>   bar(tab);   // 2
>> }
>>
>> 1. fails, 2. is OK.
>>
>> What exactly makes one fail and the other succeed? What are the allowed
>> conversions in which context?
>> Please refer to standard paragraphs for justification.
>
> 14.8.2.1 1-3:
> "Template argument deduction is done by comparing each function template
> parameter type (call it P) with the type of the corresponding argument of
> the call (call it A) as described below.
>
> If P is not a reference type:
>     If A is an array type, the pointer type produced by the array-to-pointer
> standard conversion (4.2) is used in place of A for type deduction; otherwise,
>     If A is a function type, the pointer type produced by the
> function-to-pointer standard conversion (4.3) is used in place of A for
> type deduction; otherwise,
>     If A is a cv-qualified type, the top level cv-qualifiers of A   s type are
> ignored for type deduction."
>
> The first bullet covers your case. Array-to-pointer conversion is used
> before template argument deduction.

Nope.  The function template parameter P /is/ a reference type.  Hence
the first point does not apply, at least not at first...

The Holy Standard then goes on to state

   "If P is a cv-qualified type, the top level cv-qualifiers of P's type
   are ignored for type deduction.  If P is a reference type, the type
   referred to by P is used for type deduction".

The last sentence means P = const E (&)[5] is first transformed to
P = const E[5], then this is matched against the argument type
A = int[5].

Apparently there can be no type E such that const E = int, and that
would then be a formalistic argument that the foo call above should not
compile.

But consider

   template< typename E >
   void bar( E const * ) {}

   int main()
   {
       int x;
       bar( &x );
   }

This is /accepted/ by Comeau Online (Comeau being the only compiler I
know that does not accept the OP's foo call).  And it is accepted by
MSVC 7.1 and by g++ 3.4.4, in sum, by the three compilers I tested this
on.  So apparently there's no problem matching const E = int, but it
beats me where in the standard we get that from.

And I think that was the OP's point (the E const* example in the OP's
post indicates that).

Conclusion: compilers disagree, with as far as I know Comeau the only
one that doesn't accept the common idiom, and so far no posting in this
thread has demonstrated clearly which is correct, if any.  I think this
is simply a bug in the Comeau compiler or perhaps the EDG front end.
And if not, then it's a bug in the standard... ;-)

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

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: no.spam@no.spam.com (Maciej Sobczak)
Date: Mon, 20 Feb 2006 19:24:30 GMT
Raw View
Victor Bazarov wrote:

>> Consider:
>>
>> template<class E>
>> void foo(const E (&tab)[5]) {}
>>
>> template<class E>
>> void bar(const E *) {}
>>
>> int main()
>> {
>>   char tab[5];
>>   foo(tab);   // 1
>>   bar(tab);   // 2
>> }
>>
>> 1. fails, 2. is OK.
>>
>> When I try to explain 1, my reasoning breaks down because of 2.
>> And the other way round.
>>
>> What exactly makes one fail and the other succeed? What are the
>> allowed conversions in which context?
>> Please refer to standard paragraphs for justification.
>
>
> 14.2.8.4/9
>
> "T*" is there, "T(&)[]" is not.

Then, why the following works?

template<class E>
void foo(E (&tab)[5]) {}

Above, E will be correctly deduced.

In the case of array, it's const which makes a difference between
failure and success. With pointers, const does not seem to be an
obstacle at all.
I still don't see any convincing explanation for the failure in one
place and success in another.


--
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: v.Abazarov@comAcast.net ("Victor Bazarov")
Date: Tue, 21 Feb 2006 00:10:09 GMT
Raw View
Maciej Sobczak wrote:
> Victor Bazarov wrote:
>
>>> Consider:
>>>
>>> template<class E>
>>> void foo(const E (&tab)[5]) {}
>>>
>>> template<class E>
>>> void bar(const E *) {}
>>>
>>> int main()
>>> {
>>>   char tab[5];
>>>   foo(tab);   // 1
>>>   bar(tab);   // 2
>>> }
>>>
>>> 1. fails, 2. is OK.
>>>
>>> When I try to explain 1, my reasoning breaks down because of 2.
>>> And the other way round.
>>>
>>> What exactly makes one fail and the other succeed? What are the
>>> allowed conversions in which context?
>>> Please refer to standard paragraphs for justification.
>>
>>
>> 14.2.8.4/9
>>
>> "T*" is there, "T(&)[]" is not.
>
> Then, why the following works?

Probably because your 'tab' is an array of _non_const_ char.  Just
a possibility, really.  A glimpse of a hint of an idea, really.

> template<class E>
> void foo(E (&tab)[5]) {}
>
> Above, E will be correctly deduced.
>
> In the case of array, it's const which makes a difference between
> failure and success. With pointers, const does not seem to be an
> obstacle at all.

There is no conversion between an array of non-const [T] and
a reference to an array of const [T].  There is, however, conversion
between a pointer to T and a pointer to const T.

> I still don't see any convincing explanation for the failure in one
> place and success in another.

I don't seem to have any.

V
--
Please remove capital As from my address when replying by mail


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Alf P. Steinbach" <alfps@start.no>
Date: Tue, 21 Feb 2006 09:15:55 CST
Raw View
* Victor Bazarov:
>
> There is no conversion between an array of non-const [T] and
> a reference to an array of const [T].

It seems current compilers are not as enlightened... ;-)

Consider

     int main()
     {
         int a[5] = {};
         int const (&ra)[5] = a;
     }

This compiles fine with MSVC 7.1, g++ 3.4.4, and Comeau Online 4.3.3.

Perhaps you  --  or someone  --  could provide some reference(s) to the
standard why all three compilers are wrong?

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

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Tom Widmer" <tom_usenet@hotmail.com>
Date: Tue, 21 Feb 2006 09:17:31 CST
Raw View
Maciej Sobczak wrote:
> Hi,
>
> Consider:
>
> template<class E>
> void foo(const E (&tab)[5]) {}
>
> template<class E>
> void bar(const E *) {}
>
> int main()
> {
>    char tab[5];
>    foo(tab);   // 1
>    bar(tab);   // 2
> }
>
> 1. fails, 2. is OK.
>
> When I try to explain 1, my reasoning breaks down because of 2.
> And the other way round.
>
> What exactly makes one fail and the other succeed? What are the allowed
> conversions in which context?
> Please refer to standard paragraphs for justification.

The important bits for bar(tab) are 14.8.2.1/2 first bullet and /3, 2nd
bullet:
1 "If A is an array type, the pointer type produced by the
array-to-pointer standard conversion (4.2) is used in place of A for
type deduction; otherwise,"

2 "A can be another pointer or pointer to member type that can be
converted to the deduced A via a qualification conversion (4.4)."

So, A is char[5], so char* is used by 1. But P is const E*, so E ==
char is deduced, since 2 allows a difference in cv qualification for
pointers.

As for foo, others have already explained that.

Tom

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: no.spam@no.spam.com (Maciej Sobczak)
Date: Thu, 23 Feb 2006 15:58:05 GMT
Raw View
Victor Bazarov wrote:

>>>14.2.8.4/9
>>>
>>>"T*" is there, "T(&)[]" is not.
>>
>>Then, why the following works?
>
> Probably because your 'tab' is an array of _non_const_ char.  Just
> a possibility, really.  A glimpse of a hint of an idea, really.

> There is no conversion between an array of non-const [T] and
> a reference to an array of const [T].  There is, however, conversion
> between a pointer to T and a pointer to const T.

typedef int array_of_int[5];
typedef int const array_of_const_int[5];

void foo(array_of_const_int &) {}
void bar(

int main()
{
      array_of_int a = {1, 2, 3, 4, 5};

      array_of_const_int &ca = a;  // OK
      foo(a);  // OK
}


The above works fine (Comeau, g++), so the compiler is able to bind a
reference to an array of const given an array of non-const.

I'm asking what prevents it from deducing the type when templates are
involved. It's not the lack of conversion.


>>I still don't see any convincing explanation for the failure in one
>>place and success in another.
>
> I don't seem to have any.

I don't seem to see it.


--
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Tom Widmer" <tom_usenet@hotmail.com>
Date: Thu, 23 Feb 2006 21:49:59 CST
Raw View
Maciej Sobczak wrote:

> typedef int array_of_int[5];
> typedef int const array_of_const_int[5];
>
> void foo(array_of_const_int &) {}
> void bar(
>
> int main()
> {
>       array_of_int a = {1, 2, 3, 4, 5};
>
>       array_of_const_int &ca = a;  // OK
>       foo(a);  // OK
> }
>
>
> The above works fine (Comeau, g++), so the compiler is able to bind a
> reference to an array of const given an array of non-const.
>
> I'm asking what prevents it from deducing the type when templates are
> involved. It's not the lack of conversion.

14.8.2.1/3 denotes the cases when the deduced type can differ from the
argument type (after the tranformations in 14.8.2/2 have been applied).
Pointers are allowed to require cv qualification adjustment (bullet 2)
but non-pointers aren't.

Tom

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: no.spam@no.spam.com (Maciej Sobczak)
Date: Fri, 17 Feb 2006 15:41:14 GMT
Raw View
Hi,

Consider:

template<class E>
void foo(const E (&tab)[5]) {}

template<class E>
void bar(const E *) {}

int main()
{
   char tab[5];
   foo(tab);   // 1
   bar(tab);   // 2
}

1. fails, 2. is OK.

When I try to explain 1, my reasoning breaks down because of 2.
And the other way round.

What exactly makes one fail and the other succeed? What are the allowed
conversions in which context?
Please refer to standard paragraphs for justification.

Regards,

--
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]