Topic: Possible defect: Matching of partial specializations


Author: wmm@fastdial.net
Date: 2000/10/16
Raw View
In article <6kOF5.16029$Rb4.973529@typhoon.nyroc.rr.com>,
  "Douglas Gregor" <gregod@rpi.edu> wrote:
> Consider the following code snippet regarding the size of "array". If
> the primary "Foo" template is chosen, the code is ill-formed because
> "array" is of zero length. If the partial specialization is chosen,
the
> code will compile correctly.
>
> template<typename T> struct Trait { typedef int type; };
>
> template<typename T, typename U>  struct Foo { enum {i = 0}; };
>
> template<typename T>
> struct Foo<T, typename Trait<T>::type> { enum {i=1}; };
>
> int array[Foo<int, float>::i];
>
> Consider which template is chosen for the Foo<int, float>
instantiation:
>
> In 4.5.4.1/2, the C++ Standard states that a partial
> specialization matches a given actual template argument list if all
of the
> partial specialization's template parameters can be deduced from the
given
> actual template argument list, but makes no mention of nondeduced
> contexts, implying that "typename Trait<T>::type" (which is int when
> T=int) need not be compared against "float". Thus, the partial
> specialization of Foo matches. 14.5.4.1/2 refers to 14.8.2, which
> describes template argument deduction:
>
> 14.8.2.4/1 states that for each P/A pair, P must be compatible with A
once
> the deduced values have been substituted into P, but 14.8.2.4/2 states
> that type deduction for each P/A pair is done independently, so no
> comparison between typename Trait<T>::type and float will be done
during
> type deduction (since T is in a nondeduced context).
>
> 14.8.2.4/3 seems to imply that a comparison must be made: "In certain
> contexts, however, a value does not participate in type deduction, but
> instead uses the values of template arguments that were either deduced
> elsewhere or explicitly specified." This sentence flatly contradicts
> 14.8.2.4/2 (since now each P/A pair isn't independently deduced), but
> seems to imply that P (with substitutions) must be comparable to A
even if
> no deduction has taken place within that P/A pair.

There's no contradiction here, IMHO.  The way I understand all
of this, you do deduction on each P/A pair independently.  For
each P/A pair, you end up with a template argument list.  Some
or all of the template arguments may be unspecified by the
deduction on a given P/A pair, either because the P didn't use
a given template parameter or because it was used only in a
non-deduced context.

Once you have done deduction on all the P/A pairs, you then
check the results for:

1) consistency: all the P/A pairs that deduced a given
argument must have deduced the same value (14.8.2.4p2)

2) completeness: every template parameter must have a template
argument, either deduced or specified (14.8.2.4p2)

3) compatibility: substituting the template argument list into
each P must result in a type that is compatible with the
corresponding A (14.8.2.4p1)

Check #3 is what causes the argument deduction in the partial
specialization in this example to fail.

> Overall, I can't tell if this is a defect in the standard. I've tried
the
> Comeau compiler, which rejects the code (since typename
Trait<T>::type is
> not comparable to float, so the array size is zero) and the GNU
compiler
> (g++ 2.95.2), which accepts the code (no comparison between
> typename Trait<T>::type and float is ever done, so the partial
specialization
> is taken and the array is of length 1).

I believe that the Comeau compiler is correct and that there
is no defect in the Standard.

--
William M. Miller, wmm@fastdial.net
Vignette Corporation (www.vignette.com)


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ 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.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: "Douglas Gregor" <gregod@rpi.edu>
Date: Sun, 15 Oct 2000 03:27:06 GMT
Raw View
Consider the following code snippet regarding the size of "array". If
the primary "Foo" template is chosen, the code is ill-formed because
"array" is of zero length. If the partial specialization is chosen, the
code will compile correctly.

template<typename T> struct Trait { typedef int type; };

template<typename T, typename U>  struct Foo { enum {i = 0}; };

template<typename T>
struct Foo<T, typename Trait<T>::type> { enum {i=1}; };

int array[Foo<int, float>::i];

Consider which template is chosen for the Foo<int, float> instantiation:

In 4.5.4.1/2, the C++ Standard states that a partial
specialization matches a given actual template argument list if all of the
partial specialization's template parameters can be deduced from the given
actual template argument list, but makes no mention of nondeduced
contexts, implying that "typename Trait<T>::type" (which is int when
T=int) need not be compared against "float". Thus, the partial
specialization of Foo matches. 14.5.4.1/2 refers to 14.8.2, which
describes template argument deduction:

14.8.2.4/1 states that for each P/A pair, P must be compatible with A once
the deduced values have been substituted into P, but 14.8.2.4/2 states
that type deduction for each P/A pair is done independently, so no
comparison between typename Trait<T>::type and float will be done during
type deduction (since T is in a nondeduced context).

14.8.2.4/3 seems to imply that a comparison must be made: "In certain
contexts, however, a value does not participate in type deduction, but
instead uses the values of template arguments that were either deduced
elsewhere or explicitly specified." This sentence flatly contradicts
14.8.2.4/2 (since now each P/A pair isn't independently deduced), but
seems to imply that P (with substitutions) must be comparable to A even if
no deduction has taken place within that P/A pair.

Overall, I can't tell if this is a defect in the standard. I've tried the
Comeau compiler, which rejects the code (since typename Trait<T>::type is
not comparable to float, so the array size is zero) and the GNU compiler
(g++ 2.95.2), which accepts the code (no comparison between
typename Trait<T>::type and float is ever done, so the partial specialization
is taken and the array is of length 1).

My inclination is that the Comeau compiler is correct, because otherwise
we have given a second parameter of "float", but inside the partial
specialization of Foo we can't ever get back to that "float". It bothers me
that, if the g++ compiler is correct, Foo<int, double> and Foo<int, float>
instantiate in the same way, and the 2nd template argument (double or
float) is completely lost, e.g.,

template<typename T>
struct Foo<T, typename Trait<T>::type> {
  typedef typename Trait<T>::type second_arg_type;
};

second_arg_type will be int for either Foo<int, float> or Foo<int, double>,
and we have NO way to find out which one we originally used.

Any insight into this matter would be greatly appreciated.

 Doug Gregor

---
[ 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.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]