This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH: DR150/PR9737


It was recently pointed out to me that:

  struct Dense { static const unsigned int dim = 1; };

  template <template <typename> class View,
	    typename Block>
  void operator+(float, View<Block> const&);

  template <typename Block, 
	    unsigned int Dim = Block::dim>
  struct Lvalue_proxy { operator float() const; };

  void
  test_1d (void) {
    Lvalue_proxy<Dense> p;
    float b;
    b + p;
  }

caused G++ to crash.  The proximate cause was that in binding
Lvalue_proxy to View, we needed to work out the value for the Dim
parameter, and we forgot that Block was dependent, so instead of
substituting into Block, we just went poking at its "members", which
caused us to crash.

However, that's not really the root issue.  The root issue is that G++
allows templates with N + K template parameters to bind to a template
template parameter with N parameters, if the K additional arguments
have default values.  That is in contradiction to DR150, which clearly
indicates that the template must have the same number of parameters as
the template template parameter.  

This test case is interesting in that it shows that permitting default
arguments to match is not purely an extension; it also changes the
meaning of conforming programs.  In particular, if the template
operator+ is permitted to match, then it will be called; otherwise,
the conversion operator on Lvalue_proxy will be used, and ordinary
addition on floats will be performed.

We've clearly established the policy that any G++ extensions should
not change the meaning of conforming programs.  Therefore, this
extension has to go.  The attached patch removes the extension.

The test case above ICEs with G++ 3.4 and later; it does not ICE with
GCC 3.3.2.  The ICE, therefore, is a regression.  I'd love to remove
this extension for G++ 4.1, but I don't think that's safe at this late
date; therefore, I'm going to fix the handling of the Block parameter
there and on the 4.0 branch.  

When I check in this change for mainline, I will also update the 4.2
documentation to reflect the extension being removed, and I will
update the 4.1 documentation to indicate that the feature is
deprecated.

I've not yet checked this in, as there is a minor libstdc++ testsuite
change required, and I wanted to allow those folks time to comment on
that change.  I plan to perform the check-in on Sunday.

Tested on x86_64-unknown-linux-gnu.

--
Mark Mitchell
CodeSourcery
mark@codesourcery.com
(650) 331-3385 x713

2006-02-03  Mark Mitchell  <mark@codesourcery.com>

	PR c++/9737
	* pt.c (coerce_template_template_parms): Do not templates with
	excess default arguments to match template template parameters
	with fewer parameters.
	(coerce_template_parms): Add use_default_args parameter; use
	default arguments only when true.
	(lookup_template_class): Adjust call to coerce_template_parms.
	(fn_type_unification): Likewise.
	(unify): Likewise.
	(get_bindings): Likewise.
	(dependent_type_p): Add assertions.

2006-02-03  Mark Mitchell  <mark@codesourcery.com>

	* g++.dg/template/ttp15.C: New test.
	* g++.dg/template/ttp16.C: Likewise.
	* g++.dg/template/ttp17.C: Likewise.
	* g++.old-deja/g++.pt/ttp36.C: Remove.
	* g++.old-deja/g++.pt/ttp19.C: Likewise.
	* g++.old-deja/g++.pt/ttp37.C: Likewise.
	* g++.old-deja/g++.pt/ttp38.C: Likewise.
	* g++.old-deja/g++.pt/ttp39.C: Likewise.
	* g++.old-deja/g++.pt/ttp9.C: Likewise.
	* g++.old-deja/g++.pt/ttp40.C: Likewise.
	* g++.old-deja/g++.pt/ttp51.C: Likewise.
	* g++.old-deja/g++.pt/ttp26.C: Likewise.
	* g++.old-deja/g++.pt/ttp36.C: Likewise.

2006-02-03  Mark Mitchell  <mark@codesourcery.com>

	* testsuite/testsuite_tr1.h (test_property): New function.
	* testsuite/tr1/4_metaprogramming/type_properties/extent/extent.cc 
	(test01) 

Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 110466)
+++ gcc/cp/pt.c	(working copy)
@@ -102,7 +102,8 @@ static tree classtype_mangled_name (tree
 static char* mangle_class_name_for_template (const char *, tree, tree);
 static tree tsubst_initializer_list (tree, tree);
 static tree get_class_bindings (tree, tree, tree);
-static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t, int);
+static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t, 
+				   bool, bool);
 static void tsubst_enum	(tree, tree, tree);
 static tree add_to_template_args (tree, tree);
 static tree add_outermost_template_args (tree, tree);
@@ -3718,17 +3719,12 @@ convert_nontype_argument (tree type, tre
    vectors of TREE_LIST nodes containing TYPE_DECL, TEMPLATE_DECL
    or PARM_DECL.
 
-   ARG_PARMS may contain more parameters than PARM_PARMS.  If this is
-   the case, then extra parameters must have default arguments.
-
    Consider the example:
-     template <class T, class Allocator = allocator> class vector;
-     template<template <class U> class TT> class C;
+     template <class T> class A;
+     template<template <class U> class TT> class B;
 
-   C<vector> is a valid instantiation.  PARM_PARMS for the above code
-   contains a TYPE_DECL (for U),  ARG_PARMS contains two TYPE_DECLs (for
-   T and Allocator) and OUTER_ARGS contains the argument that is used to
-   substitute the TT parameter.  */
+   For B<A>, PARM_PARMS are the parameters to TT, while ARG_PARMS are
+   the parameters to A, and OUTER_ARGS contains A.  */
 
 static int
 coerce_template_template_parms (tree parm_parms,
@@ -3746,10 +3742,7 @@ coerce_template_template_parms (tree par
   nparms = TREE_VEC_LENGTH (parm_parms);
   nargs = TREE_VEC_LENGTH (arg_parms);
 
-  /* The rule here is opposite of coerce_template_parms.  */
-  if (nargs < nparms
-      || (nargs > nparms
-	  && TREE_PURPOSE (TREE_VEC_ELT (arg_parms, nparms)) == NULL_TREE))
+  if (nargs != nparms)
     return 0;
 
   for (i = 0; i < nparms; ++i)
@@ -3988,17 +3981,20 @@ convert_template_argument (tree parm,
    arguments.  If any error occurs, return error_mark_node. Error and
    warning messages are issued under control of COMPLAIN.
 
-   If REQUIRE_ALL_ARGUMENTS is nonzero, all arguments must be
-   provided in ARGLIST, or else trailing parameters must have default
-   values.  If REQUIRE_ALL_ARGUMENTS is zero, we will attempt argument
-   deduction for any unspecified trailing arguments.  */
+   If REQUIRE_ALL_ARGS is false, argument deduction will be performed
+   for arugments not specified in ARGS.  Otherwise, if
+   USE_DEFAULT_ARGS is true, default arguments will be used to fill in
+   unspecified arguments.  If REQUIRE_ALL_ARGS is true, but
+   USE_DEFAULT_ARGS is false, then all arguments must be specified in
+   ARGS.  */
 
 static tree
 coerce_template_parms (tree parms,
 		       tree args,
 		       tree in_decl,
 		       tsubst_flags_t complain,
-		       int require_all_arguments)
+		       bool require_all_args,
+		       bool use_default_args)
 {
   int nparms, nargs, i, lost = 0;
   tree inner_args;
@@ -4011,8 +4007,9 @@ coerce_template_parms (tree parms,
 
   if (nargs > nparms
       || (nargs < nparms
-	  && require_all_arguments
-	  && TREE_PURPOSE (TREE_VEC_ELT (parms, nargs)) == NULL_TREE))
+	  && require_all_args
+	  && (!use_default_args
+	      || !TREE_PURPOSE (TREE_VEC_ELT (parms, nargs)))))
     {
       if (complain & tf_error)
 	{
@@ -4039,7 +4036,7 @@ coerce_template_parms (tree parms,
       /* Calculate the Ith argument.  */
       if (i < nargs)
 	arg = TREE_VEC_ELT (inner_args, i);
-      else if (require_all_arguments)
+      else if (require_all_args)
 	/* There must be a default arg in this case.  */
 	arg = tsubst_template_arg (TREE_PURPOSE (parm), new_args,
 				   complain, in_decl);
@@ -4444,7 +4441,9 @@ lookup_template_class (tree d1,
 	arglist = add_to_template_args (current_template_args (), arglist);
 
       arglist2 = coerce_template_parms (parmlist, arglist, template,
-					complain, /*require_all_args=*/1);
+					complain, 
+					/*require_all_args=*/true,
+					/*use_default_args=*/true);
       if (arglist2 == error_mark_node
 	  || (!uses_template_parms (arglist2)
 	      && check_instantiated_args (template, arglist2, complain)))
@@ -4513,7 +4512,9 @@ lookup_template_class (tree d1,
 	    {
 	      tree a = coerce_template_parms (TREE_VALUE (t),
 					      arglist, template,
-					      complain, /*require_all_args=*/1);
+					      complain, 
+					      /*require_all_args=*/true,
+					      /*use_default_args=*/true);
 
 	      /* Don't process further if one of the levels fails.  */
 	      if (a == error_mark_node)
@@ -4542,7 +4543,9 @@ lookup_template_class (tree d1,
 	  = coerce_template_parms (INNERMOST_TEMPLATE_PARMS (parmlist),
 				   INNERMOST_TEMPLATE_ARGS (arglist),
 				   template,
-				   complain, /*require_all_args=*/1);
+				   complain, 
+				   /*require_all_args=*/true,
+				   /*use_default_args=*/true);
 
       if (arglist == error_mark_node)
 	/* We were unable to bind the arguments.  */
@@ -9237,7 +9240,8 @@ fn_type_unification (tree fn,
       converted_args
 	= (coerce_template_parms (DECL_INNERMOST_TEMPLATE_PARMS (fn),
 				  explicit_targs, NULL_TREE, tf_none,
-				  /*require_all_arguments=*/0));
+				  /*require_all_args=*/false,
+				  /*use_default_args=*/false));
       if (converted_args == error_mark_node)
 	return 1;
 
@@ -10003,21 +10007,44 @@ unify (tree tparms, tree targs, tree par
 	    return 1;
 
 	  {
-	    tree parmtmpl = TYPE_TI_TEMPLATE (parm);
 	    tree parmvec = TYPE_TI_ARGS (parm);
 	    tree argvec = INNERMOST_TEMPLATE_ARGS (TYPE_TI_ARGS (arg));
 	    tree argtmplvec
 	      = DECL_INNERMOST_TEMPLATE_PARMS (TYPE_TI_TEMPLATE (arg));
 	    int i;
 
-	    /* The parameter and argument roles have to be switched here
-	       in order to handle default arguments properly.  For example,
-	       template<template <class> class TT> void f(TT<int>)
-	       should be able to accept vector<int> which comes from
-	       template <class T, class Allocator = allocator>
-	       class vector.  */
+	    /* The resolution to DR150 makes clear that default
+	       arguments for an N-argument may not be used to bind T
+	       to a template template parameter with fewer than N
+	       parameters.  It is not safe to permit the binding of
+	       default arguments as an extension, as that may change
+	       the meaning of a conforming program.  Consider:
+
+		  struct Dense { static const unsigned int dim = 1; };
+
+		  template <template <typename> class View,
+			    typename Block>
+		  void operator+(float, View<Block> const&);
+
+		  template <typename Block, 
+		            unsigned int Dim = Block::dim>
+		  struct Lvalue_proxy { operator float() const; };
+
+		  void
+		  test_1d (void) {
+		    Lvalue_proxy<Dense> p;
+		    float b;
+		    b + p;
+		  }
 
-	    if (coerce_template_parms (argtmplvec, parmvec, parmtmpl, 0, 1)
+	      Here, if Lvalue_proxy is permitted to bind to View, then
+	      the global operator+ will be used; if they are not, the
+	      Lvalue_proxy will be converted to float.  */	  
+	    if (coerce_template_parms (argtmplvec, parmvec, 
+				       TYPE_TI_TEMPLATE (parm),
+				       tf_none,
+				       /*require_all_args=*/true,
+				       /*use_default_args=*/false)
 		== error_mark_node)
 	      return 1;
 
@@ -10733,9 +10760,11 @@ get_bindings (tree fn, tree decl, tree e
 	return NULL_TREE;
 
       converted_args
-	= (coerce_template_parms (DECL_INNERMOST_TEMPLATE_PARMS (tmpl),
-				  explicit_args, NULL_TREE,
-				  tf_none, /*require_all_arguments=*/0));
+	= coerce_template_parms (DECL_INNERMOST_TEMPLATE_PARMS (tmpl),
+				 explicit_args, NULL_TREE,
+				 tf_none, 
+				 /*require_all_args=*/false,
+				 /*use_default_args=*/false);
       if (converted_args == error_mark_node)
 	return NULL_TREE;
 
@@ -12216,7 +12245,13 @@ dependent_type_p (tree type)
   /* If there are no template parameters in scope, then there can't be
      any dependent types.  */
   if (!processing_template_decl)
-    return false;
+    {
+      /* If we are not processing a template, then nobody should be
+	 providing us with a dependent type.  */
+      gcc_assert (type);
+      gcc_assert (TREE_CODE (type) != TEMPLATE_TYPE_PARM);
+      return false;
+    }
 
   /* If the type is NULL, we have not computed a type for the entity
      in question; in that case, the type is dependent.  */
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp36.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp36.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp36.C	(working copy)
@@ -1,30 +0,0 @@
-// { dg-do run  }
-template<int T, class U = int> class D
-{
-	public:
-		int f();
-};
-
-template<int T, class U> int D<T,U>::f()
-{
-	return T+sizeof(U);
-}
-
-template<template<int> class D,class E> class C
-{
-		D<1> d;
-	public:
-		int f() { return d.f(); }
-};
-
-template<template<int> class D> int f(D<1> &d1)
-{
-	d1.f();
-	return 0;
-}
-
-int main()
-{
-	D<1> c1;
-	f(c1);
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp19.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp19.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp19.C	(working copy)
@@ -1,25 +0,0 @@
-// { dg-do run  }
-#include <vector>
-
-template<template<class> class D,class E> class C
-{
-		D<E> d;
-	public:
-		int size() { return d.size(); }
-};
-
-template<template<class> class D,class E> int size(D<E> &d1)
-{
-	d1.size();
-	C<D,E> d2;
-	d2.size();
-	return 0;
-}
-
-int main()
-{
-	std::vector<int> c1;
-	std::vector<char> c2;
-	size(c1);
-	size(c2);
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp37.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp37.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp37.C	(working copy)
@@ -1,30 +0,0 @@
-// { dg-do run  }
-template<int T, class U = int> class D
-{
-	public:
-		int f();
-};
-
-template<int T, class U> int D<T,U>::f()
-{
-	return T+sizeof(U);
-}
-
-template<template<int> class D,class E> class C
-{
-		D<1> d;
-	public:
-		int f() { return d.f(); }
-};
-
-template<template<int> class D, int T> int f(D<T> &d1)
-{
-	d1.f();
-	return T;
-}
-
-int main()
-{
-	D<1> c1;
-	f(c1);
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp38.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp38.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp38.C	(working copy)
@@ -1,30 +0,0 @@
-// { dg-do run  }
-template<class T, class U = int> class D
-{
-	public:
-		int f();
-};
-
-template<class T, class U> int D<T,U>::f()
-{
-	return sizeof(T)+sizeof(U);
-}
-
-template<template<class> class D,class E> class C
-{
-		D<E> d;
-	public:
-		int f() { return d.f(); }
-};
-
-template<template<class> class D> int f(D<int> &d1)
-{
-	d1.f();
-	return 0;
-}
-
-int main()
-{
-	D<int> c1;
-	f(c1);
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp39.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp39.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp39.C	(working copy)
@@ -1,30 +0,0 @@
-// { dg-do run  }
-template<class T, class U = T> class D
-{
-	public:
-		int f();
-};
-
-template<class T, class U> int D<T,U>::f()
-{
-	return sizeof(T)+sizeof(U);
-}
-
-template<template<class> class D,class E> class C
-{
-		D<E> d;
-	public:
-		int f() { return d.f(); }
-};
-
-template<template<class> class D> int f(D<int> &d1)
-{
-	d1.f();
-	return 0;
-}
-
-int main()
-{
-	D<int> c1;
-	f(c1);
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp9.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp9.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp9.C	(working copy)
@@ -1,14 +0,0 @@
-// { dg-do run  }
-template<class E,class F=int> class D
-{
-};
-
-template<template<class> class D,class E> class C
-{
-	D<E>	d;
-};
-
-int main()
-{
-	C<D,int> c;
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp40.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp40.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp40.C	(working copy)
@@ -1,21 +0,0 @@
-// { dg-do run  }
-#include <vector>
-
-template<class E,template<class> class DD = std::vector> class C
-{
-		DD<E> d;
-	public:
-		int f();
-};
-
-template<class E,template<class> class DD> int C<E,DD>::f()
-{
-	DD<E> d2;
-	return d2.size();
-}
-
-int main()
-{
-	C<int> c;
-	c.f();
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp51.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp51.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp51.C	(working copy)
@@ -1,14 +0,0 @@
-// { dg-do run  }
-template<class E, int i, class F, class G=int, int j=i, class H=E> class D
-{
-};
-
-template<template<class,int,class,class> class D,class E> class C
-{
-	D<E,2,char,bool>	d;
-};
-
-int main()
-{
-	C<D,int> c;
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp26.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp26.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp26.C	(working copy)
@@ -1,34 +0,0 @@
-// { dg-do run  }
-template<class T, class U = int> class D
-{
-	public:
-		int f();
-};
-
-template<class T, class U> int D<T,U>::f()
-{
-	return sizeof(T)+sizeof(U);
-}
-
-template<template<class> class D,class E> class C
-{
-		D<E> d;
-	public:
-		int f() { return d.f(); }
-};
-
-template<template<class> class D,class E> int f(D<E> &d1)
-{
-	d1.f();
-	C<D,E> d2;
-	d2.f();
-	return 0;
-}
-
-int main()
-{
-	D<int> c1;
-	D<char> c2;
-	f(c1);
-	f(c2);
-}
Index: gcc/testsuite/g++.old-deja/g++.pt/ttp35.C
===================================================================
--- gcc/testsuite/g++.old-deja/g++.pt/ttp35.C	(revision 110465)
+++ gcc/testsuite/g++.old-deja/g++.pt/ttp35.C	(working copy)
@@ -1,36 +0,0 @@
-// { dg-do run  }
-template<int T, class U = int> class D
-{
-	public:
-		int f();
-};
-
-template<int T, class U> int D<T,U>::f()
-{
-	return T+sizeof(U);
-}
-
-template<template<int> class D,class E> class C
-{
-		D<1> d;
-	public:
-		int f() { return d.f(); }
-};
-
-template<template<int> class D> int f(D<2> &d1)
-{
-	d1.f();
-	return 0;
-}
-
-template<template<int> class D> int f(D<1> &d1)
-{
-	d1.f();
-	return 0;
-}
-
-int main()
-{
-	D<1> c1;
-	f(c1);
-}
Index: gcc/testsuite/g++.dg/template/ttp15.C
===================================================================
--- gcc/testsuite/g++.dg/template/ttp15.C	(revision 0)
+++ gcc/testsuite/g++.dg/template/ttp15.C	(revision 0)
@@ -0,0 +1,21 @@
+struct Dense {
+  static const unsigned int dim = 1;
+};
+
+template <template <typename> class View,
+	  typename Block>
+void operator+(float, View<Block> const&);
+
+template <typename Block,
+	  unsigned int Dim = Block::dim>
+struct Lvalue_proxy {
+  operator float() const;
+};
+
+void
+test_1d (void)
+{
+  Lvalue_proxy<Dense> p;
+  float b;
+  b + p;
+}
Index: gcc/testsuite/g++.dg/template/ttp16.C
===================================================================
--- gcc/testsuite/g++.dg/template/ttp16.C	(revision 0)
+++ gcc/testsuite/g++.dg/template/ttp16.C	(revision 0)
@@ -0,0 +1,7 @@
+template <template <typename> class C>
+void f() {}
+
+template <typename T, typename U = int>
+struct S {};
+
+template void f<S>(); // { dg-error "match" }
Index: gcc/testsuite/g++.dg/template/ttp17.C
===================================================================
--- gcc/testsuite/g++.dg/template/ttp17.C	(revision 0)
+++ gcc/testsuite/g++.dg/template/ttp17.C	(revision 0)
@@ -0,0 +1,7 @@
+template <template <typename> class C>
+void f(C<double>) {}
+
+template <typename T, typename U = int>
+struct S {};
+
+template void f(S<double>); // { dg-error "match" }
Index: libstdc++-v3/testsuite/testsuite_tr1.h
===================================================================
--- libstdc++-v3/testsuite/testsuite_tr1.h	(revision 110465)
+++ libstdc++-v3/testsuite/testsuite_tr1.h	(working copy)
@@ -64,6 +64,18 @@ namespace __gnu_test
       return ret;
     }
 
+  template<template<typename, unsigned> class Property,
+           typename Type,
+	   unsigned Uint>
+    bool
+    test_property(typename Property<Type, Uint>::value_type value)
+    {
+      bool ret = true;
+      ret &= Property<Type, Uint>::value == value;
+      ret &= Property<Type, Uint>::type::value == value;
+      return ret;
+    }
+
   template<template<typename, typename> class Relationship,
            typename Type1, typename Type2>
     bool
Index: libstdc++-v3/testsuite/tr1/4_metaprogramming/type_properties/extent/extent.cc
===================================================================
--- libstdc++-v3/testsuite/tr1/4_metaprogramming/type_properties/extent/extent.cc	(revision 110465)
+++ libstdc++-v3/testsuite/tr1/4_metaprogramming/type_properties/extent/extent.cc	(working copy)
@@ -30,19 +30,19 @@ void test01()
   using std::tr1::extent;
   using namespace __gnu_test;
 
-  VERIFY( (test_property<extent, int>(0)) );
-  VERIFY( (test_property<extent, int[2]>(2)) );
-  VERIFY( (test_property<extent, int[2][4]>(2)) );
-  VERIFY( (test_property<extent, int[][4]>(0)) );
+  VERIFY( (test_property<extent, int, 0>(0)) );
+  VERIFY( (test_property<extent, int[2], 0>(2)) );
+  VERIFY( (test_property<extent, int[2][4], 0>(2)) );
+  VERIFY( (test_property<extent, int[][4], 0>(0)) );
   VERIFY( (extent<int, 1>::value == 0) );
   VERIFY( (extent<int[2], 1>::value == 0) );
   VERIFY( (extent<int[2][4], 1>::value == 4) );
   VERIFY( (extent<int[][4], 1>::value == 4) );
   VERIFY( (extent<int[10][4][6][8][12][2], 4>::value == 12) );
-  VERIFY( (test_property<extent, ClassType>(0)) );
-  VERIFY( (test_property<extent, ClassType[2]>(2)) );
-  VERIFY( (test_property<extent, ClassType[2][4]>(2)) );
-  VERIFY( (test_property<extent, ClassType[][4]>(0)) );
+  VERIFY( (test_property<extent, ClassType, 0>(0)) );
+  VERIFY( (test_property<extent, ClassType[2], 0>(2)) );
+  VERIFY( (test_property<extent, ClassType[2][4], 0>(2)) );
+  VERIFY( (test_property<extent, ClassType[][4], 0>(0)) );
   VERIFY( (extent<ClassType, 1>::value == 0) );
   VERIFY( (extent<ClassType[2], 1>::value == 0) );
   VERIFY( (extent<ClassType[2][4], 1>::value == 4) );


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]