cp-tree.h (CLASSTYPE_METHOD_VEC): Adjust comment.
1999-02-19 Mark Mitchell <mark@markmitchell.com> * cp-tree.h (CLASSTYPE_METHOD_VEC): Adjust comment. (fn_type_unification): Adjust prototype. (lookup_fnfields_1): Declare. * call.c (add_template_candidate_real): Adjust call to fn_type_unification. * class.c (add_method): Don't allow duplicate declarations of constructors or destructors. (resolve_address_of_overloaded_function): Remove unused variable. Adjust call to fn_type_unification. * decl.c (grokfndecl): Be more robust in the face of illegal specializations. * decl2.c (check_classfn): Remove hokey handling of member templates. * pt.c (determine_specialization): Improve comments. Adjust to handle template argument deduction as per the standard. (check_explicit_specialization): Fix comment spacing. Handle type-conversion operators correctly. Improve error-recovery. (fn_type_unification): Remove EXTRA_FN_ARG parameter. (get_bindings_real): Simplify handling of static members. * search.c (lookup_fnfields_1): Make it have external linkage. * typeck.c (compparms): Fix comment. (build_unary_op): Don't try to figure out which template specialization is being referred to when when the address-of operator is used with a template function. From-SVN: r25347
This commit is contained in:
parent
939d7216dc
commit
0301787454
@ -1,3 +1,30 @@
|
||||
1999-02-19 Mark Mitchell <mark@markmitchell.com>
|
||||
|
||||
* cp-tree.h (CLASSTYPE_METHOD_VEC): Adjust comment.
|
||||
(fn_type_unification): Adjust prototype.
|
||||
(lookup_fnfields_1): Declare.
|
||||
* call.c (add_template_candidate_real): Adjust call to
|
||||
fn_type_unification.
|
||||
* class.c (add_method): Don't allow duplicate declarations of
|
||||
constructors or destructors.
|
||||
(resolve_address_of_overloaded_function): Remove unused variable.
|
||||
Adjust call to fn_type_unification.
|
||||
* decl.c (grokfndecl): Be more robust in the face of illegal
|
||||
specializations.
|
||||
* decl2.c (check_classfn): Remove hokey handling of member
|
||||
templates.
|
||||
* pt.c (determine_specialization): Improve comments. Adjust to
|
||||
handle template argument deduction as per the standard.
|
||||
(check_explicit_specialization): Fix comment spacing. Handle
|
||||
type-conversion operators correctly. Improve error-recovery.
|
||||
(fn_type_unification): Remove EXTRA_FN_ARG parameter.
|
||||
(get_bindings_real): Simplify handling of static members.
|
||||
* search.c (lookup_fnfields_1): Make it have external linkage.
|
||||
* typeck.c (compparms): Fix comment.
|
||||
(build_unary_op): Don't try to figure out which template
|
||||
specialization is being referred to when when the address-of
|
||||
operator is used with a template function.
|
||||
|
||||
Thu Feb 18 23:40:01 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
|
||||
|
||||
* cp-tree.h (lvalue_or_else): Qualify a char* with the `const'
|
||||
|
@ -1948,7 +1948,7 @@ add_template_candidate_real (candidates, tmpl, explicit_targs,
|
||||
tree fn;
|
||||
|
||||
i = fn_type_unification (tmpl, explicit_targs, targs, arglist,
|
||||
return_type, strict, NULL_TREE);
|
||||
return_type, strict);
|
||||
|
||||
if (i != 0)
|
||||
return candidates;
|
||||
|
200
gcc/cp/class.c
200
gcc/cp/class.c
@ -1136,6 +1136,7 @@ add_method (type, fields, method)
|
||||
else
|
||||
{
|
||||
int len;
|
||||
int slot;
|
||||
tree method_vec;
|
||||
|
||||
if (!CLASSTYPE_METHOD_VEC (type))
|
||||
@ -1157,27 +1158,20 @@ add_method (type, fields, method)
|
||||
len = TREE_VEC_LENGTH (method_vec);
|
||||
|
||||
if (DECL_NAME (method) == constructor_name (type))
|
||||
{
|
||||
/* A new constructor or destructor. Constructors go in
|
||||
slot 0; destructors go in slot 1. */
|
||||
int slot
|
||||
= DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (method)) ? 1 : 0;
|
||||
|
||||
TREE_VEC_ELT (method_vec, slot)
|
||||
= build_overload (method, TREE_VEC_ELT (method_vec, slot));
|
||||
}
|
||||
/* A new constructor or destructor. Constructors go in
|
||||
slot 0; destructors go in slot 1. */
|
||||
slot = DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (method)) ? 1 : 0;
|
||||
else
|
||||
{
|
||||
int i;
|
||||
|
||||
/* See if we already have an entry with this name. */
|
||||
for (i = 2; i < len; ++i)
|
||||
if (!TREE_VEC_ELT (method_vec, i)
|
||||
|| (DECL_NAME (OVL_CURRENT (TREE_VEC_ELT (method_vec, i)))
|
||||
for (slot = 2; slot < len; ++slot)
|
||||
if (!TREE_VEC_ELT (method_vec, slot)
|
||||
|| (DECL_NAME (OVL_CURRENT (TREE_VEC_ELT (method_vec,
|
||||
slot)))
|
||||
== DECL_NAME (method)))
|
||||
break;
|
||||
|
||||
if (i == len)
|
||||
if (slot == len)
|
||||
{
|
||||
/* We need a bigger method vector. */
|
||||
tree new_vec = make_method_vec (2 * len);
|
||||
@ -1188,76 +1182,8 @@ add_method (type, fields, method)
|
||||
len = 2 * len;
|
||||
method_vec = CLASSTYPE_METHOD_VEC (type) = new_vec;
|
||||
}
|
||||
else if (template_class_depth (type))
|
||||
/* TYPE is a template class. Don't issue any errors now;
|
||||
wait until instantiation time to complain. */
|
||||
;
|
||||
else
|
||||
{
|
||||
tree fns;
|
||||
|
||||
/* Check to see if we've already got this method. */
|
||||
for (fns = TREE_VEC_ELT (method_vec, i);
|
||||
fns;
|
||||
fns = OVL_NEXT (fns))
|
||||
{
|
||||
tree fn = OVL_CURRENT (fns);
|
||||
|
||||
if (TREE_CODE (fn) != TREE_CODE (method))
|
||||
continue;
|
||||
|
||||
if (TREE_CODE (method) != TEMPLATE_DECL)
|
||||
{
|
||||
/* [over.load] Member function declarations with the
|
||||
same name and the same parameter types cannot be
|
||||
overloaded if any of them is a static member
|
||||
function declaration. */
|
||||
if (DECL_STATIC_FUNCTION_P (fn)
|
||||
!= DECL_STATIC_FUNCTION_P (method))
|
||||
{
|
||||
tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn));
|
||||
tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (method));
|
||||
|
||||
if (! DECL_STATIC_FUNCTION_P (fn))
|
||||
parms1 = TREE_CHAIN (parms1);
|
||||
else
|
||||
parms2 = TREE_CHAIN (parms2);
|
||||
|
||||
if (compparms (parms1, parms2))
|
||||
cp_error ("`%#D' and `%#D' cannot be overloaded",
|
||||
fn, method);
|
||||
}
|
||||
|
||||
/* Since this is an ordinary function in a
|
||||
non-template class, it's mangled name can be
|
||||
used as a unique identifier. This technique
|
||||
is only an optimization; we would get the
|
||||
same results if we just used decls_match
|
||||
here. */
|
||||
if (DECL_ASSEMBLER_NAME (fn)
|
||||
!= DECL_ASSEMBLER_NAME (method))
|
||||
continue;
|
||||
}
|
||||
else if (!decls_match (fn, method))
|
||||
continue;
|
||||
|
||||
/* There has already been a declaration of this
|
||||
method or member template. */
|
||||
cp_error_at ("`%D' has already been declared in `%T'",
|
||||
method, type);
|
||||
|
||||
/* We don't call duplicate_decls here to merege the
|
||||
declarations because that will confuse things if
|
||||
the methods have inline definitions In
|
||||
particular, we will crash while processing the
|
||||
definitions. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (TREE_VEC_ELT (method_vec, i))
|
||||
/* We found a match. */;
|
||||
else if (DECL_CONV_FN_P (method))
|
||||
if (DECL_CONV_FN_P (method) && !TREE_VEC_ELT (method_vec, slot))
|
||||
{
|
||||
/* Type conversion operators have to come before
|
||||
ordinary methods; add_conversions depends on this to
|
||||
@ -1265,42 +1191,107 @@ add_method (type, fields, method)
|
||||
necessary, we slide some of the vector elements up.
|
||||
In theory, this makes this algorithm O(N^2) but we
|
||||
don't expect many conversion operators. */
|
||||
for (i = 2; i < len; ++i)
|
||||
for (slot = 2; slot < len; ++slot)
|
||||
{
|
||||
tree fn = TREE_VEC_ELT (method_vec, i);
|
||||
|
||||
tree fn = TREE_VEC_ELT (method_vec, slot);
|
||||
|
||||
if (!fn)
|
||||
/* There are no more entries in the vector, so we
|
||||
can insert the new conversion operator here. */
|
||||
break;
|
||||
|
||||
if (! DECL_CONV_FN_P (OVL_CURRENT (fn)))
|
||||
/* We can insert the new function right at the Ith
|
||||
position. */
|
||||
|
||||
if (!DECL_CONV_FN_P (OVL_CURRENT (fn)))
|
||||
/* We can insert the new function right at the
|
||||
SLOTth position. */
|
||||
break;
|
||||
}
|
||||
|
||||
if (!TREE_VEC_ELT (method_vec, i))
|
||||
|
||||
if (!TREE_VEC_ELT (method_vec, slot))
|
||||
/* There is nothing in the Ith slot, so we can avoid
|
||||
moving anything. */
|
||||
;
|
||||
else
|
||||
{
|
||||
/* We know the last slot in the vector is empty
|
||||
because we know that at this point there's room for
|
||||
a new function. */
|
||||
bcopy ((PTR) &TREE_VEC_ELT (method_vec, i),
|
||||
(PTR) &TREE_VEC_ELT (method_vec, i + 1),
|
||||
(len - i - 1) * sizeof (tree));
|
||||
TREE_VEC_ELT (method_vec, i) = NULL_TREE;
|
||||
because we know that at this point there's room
|
||||
for a new function. */
|
||||
bcopy ((PTR) &TREE_VEC_ELT (method_vec, slot),
|
||||
(PTR) &TREE_VEC_ELT (method_vec, slot + 1),
|
||||
(len - slot - 1) * sizeof (tree));
|
||||
TREE_VEC_ELT (method_vec, slot) = NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Actually insert the new method. */
|
||||
TREE_VEC_ELT (method_vec, i)
|
||||
= build_overload (method, TREE_VEC_ELT (method_vec, i));
|
||||
}
|
||||
|
||||
if (template_class_depth (type))
|
||||
/* TYPE is a template class. Don't issue any errors now; wait
|
||||
until instantiation time to complain. */
|
||||
;
|
||||
else
|
||||
{
|
||||
tree fns;
|
||||
|
||||
/* Check to see if we've already got this method. */
|
||||
for (fns = TREE_VEC_ELT (method_vec, slot);
|
||||
fns;
|
||||
fns = OVL_NEXT (fns))
|
||||
{
|
||||
tree fn = OVL_CURRENT (fns);
|
||||
|
||||
if (TREE_CODE (fn) != TREE_CODE (method))
|
||||
continue;
|
||||
|
||||
if (TREE_CODE (method) != TEMPLATE_DECL)
|
||||
{
|
||||
/* [over.load] Member function declarations with the
|
||||
same name and the same parameter types cannot be
|
||||
overloaded if any of them is a static member
|
||||
function declaration. */
|
||||
if (DECL_STATIC_FUNCTION_P (fn)
|
||||
!= DECL_STATIC_FUNCTION_P (method))
|
||||
{
|
||||
tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn));
|
||||
tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (method));
|
||||
|
||||
if (! DECL_STATIC_FUNCTION_P (fn))
|
||||
parms1 = TREE_CHAIN (parms1);
|
||||
else
|
||||
parms2 = TREE_CHAIN (parms2);
|
||||
|
||||
if (compparms (parms1, parms2))
|
||||
cp_error ("`%#D' and `%#D' cannot be overloaded",
|
||||
fn, method);
|
||||
}
|
||||
|
||||
/* Since this is an ordinary function in a
|
||||
non-template class, it's mangled name can be used
|
||||
as a unique identifier. This technique is only
|
||||
an optimization; we would get the same results if
|
||||
we just used decls_match here. */
|
||||
if (DECL_ASSEMBLER_NAME (fn)
|
||||
!= DECL_ASSEMBLER_NAME (method))
|
||||
continue;
|
||||
}
|
||||
else if (!decls_match (fn, method))
|
||||
continue;
|
||||
|
||||
/* There has already been a declaration of this method
|
||||
or member template. */
|
||||
cp_error_at ("`%D' has already been declared in `%T'",
|
||||
method, type);
|
||||
|
||||
/* We don't call duplicate_decls here to merge the
|
||||
declarations because that will confuse things if the
|
||||
methods have inline definitions In particular, we
|
||||
will crash while processing the definitions. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Actually insert the new method. */
|
||||
TREE_VEC_ELT (method_vec, slot)
|
||||
= build_overload (method, TREE_VEC_ELT (method_vec, slot));
|
||||
|
||||
if (TYPE_BINFO_BASETYPES (type) && CLASSTYPE_BASELINK_VEC (type))
|
||||
{
|
||||
/* ??? May be better to know whether these can be extended? */
|
||||
@ -5115,7 +5106,6 @@ resolve_address_of_overloaded_function (target_type,
|
||||
for (fns = overload; fns; fns = OVL_CHAIN (fns))
|
||||
{
|
||||
tree fn = OVL_FUNCTION (fns);
|
||||
tree fn_arg_types;
|
||||
tree instantiation;
|
||||
tree instantiation_type;
|
||||
tree targs;
|
||||
@ -5134,7 +5124,7 @@ resolve_address_of_overloaded_function (target_type,
|
||||
targs = make_scratch_vec (DECL_NTPARMS (fn));
|
||||
if (fn_type_unification (fn, explicit_targs, targs,
|
||||
target_arg_types, NULL_TREE,
|
||||
DEDUCE_EXACT, NULL_TREE) != 0)
|
||||
DEDUCE_EXACT) != 0)
|
||||
/* Argument deduction failed. */
|
||||
continue;
|
||||
|
||||
|
@ -887,11 +887,11 @@ struct lang_type
|
||||
#define TYPE_USES_VIRTUAL_BASECLASSES(NODE) (TREE_LANG_FLAG_3(NODE))
|
||||
|
||||
/* Vector member functions defined in this class. Each element is
|
||||
either a FUNCTION_DECL, a TEMPLATE_DECL, or an OVERLOAD. The first
|
||||
either a FUNCTION_DECL, a TEMPLATE_DECL, or an OVERLOAD. All
|
||||
functions with the same name end up in the same slot. The first
|
||||
two elements are for constructors, and destructors, respectively.
|
||||
Any user-defined conversion operators follow these. These are
|
||||
followed by ordinary member functions. There may be empty entries
|
||||
at the end of the vector. */
|
||||
These are followed by ordinary member functions. There may be
|
||||
empty entries at the end of the vector. */
|
||||
#define CLASSTYPE_METHOD_VEC(NODE) (TYPE_LANG_SPECIFIC(NODE)->methods)
|
||||
|
||||
/* The first type conversion operator in the class (the others can be
|
||||
@ -3068,7 +3068,7 @@ extern int uses_template_parms PROTO((tree));
|
||||
extern tree instantiate_class_template PROTO((tree));
|
||||
extern tree instantiate_template PROTO((tree, tree));
|
||||
extern void overload_template_name PROTO((tree));
|
||||
extern int fn_type_unification PROTO((tree, tree, tree, tree, tree, unification_kind_t, tree));
|
||||
extern int fn_type_unification PROTO((tree, tree, tree, tree, tree, unification_kind_t));
|
||||
struct tinst_level *tinst_for_decl PROTO((void));
|
||||
extern void mark_decl_instantiated PROTO((tree, int));
|
||||
extern int more_specialized PROTO((tree, tree, tree));
|
||||
@ -3126,6 +3126,7 @@ extern int get_base_distance PROTO((tree, tree, int, tree *));
|
||||
extern tree compute_access PROTO((tree, tree));
|
||||
extern tree lookup_field PROTO((tree, tree, int, int));
|
||||
extern tree lookup_nested_field PROTO((tree, int));
|
||||
extern int lookup_fnfields_1 PROTO((tree, tree));
|
||||
extern tree lookup_fnfields PROTO((tree, tree, int));
|
||||
extern tree lookup_member PROTO((tree, tree, int, int));
|
||||
extern tree lookup_nested_tag PROTO((tree, tree));
|
||||
|
@ -8599,8 +8599,11 @@ grokfndecl (ctype, type, declarator, orig_declarator, virtualp, flags, quals,
|
||||
cp_error ("definition of implicitly-declared `%D'", tmp);
|
||||
if (tmp)
|
||||
{
|
||||
/* Attempt to merge the declarations. This can fail, in
|
||||
the case of some illegal specialization declarations. */
|
||||
if (!duplicate_decls (decl, tmp))
|
||||
my_friendly_abort (892);
|
||||
cp_error ("no `%#D' member function declared in class `%T'",
|
||||
decl, ctype);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
@ -1309,8 +1309,21 @@ check_classfn (ctype, function)
|
||||
tree method_vec = CLASSTYPE_METHOD_VEC (complete_type (ctype));
|
||||
tree *methods = 0;
|
||||
tree *end = 0;
|
||||
tree templates = NULL_TREE;
|
||||
|
||||
|
||||
if (DECL_USE_TEMPLATE (function)
|
||||
&& is_member_template (DECL_TI_TEMPLATE (function)))
|
||||
/* Since this is a specialization of a member template,
|
||||
we're not going to find the declaration in the class.
|
||||
For example, in:
|
||||
|
||||
struct S { template <typename T> void f(T); };
|
||||
template <> void S::f(int);
|
||||
|
||||
we're not going to find `S::f(int)', but there's no
|
||||
reason we should, either. We let our callers know we didn't
|
||||
find the method, but we don't complain. */
|
||||
return NULL_TREE;
|
||||
|
||||
if (method_vec != 0)
|
||||
{
|
||||
methods = &TREE_VEC_ELT (method_vec, 0);
|
||||
@ -1375,36 +1388,13 @@ check_classfn (ctype, function)
|
||||
|| (DECL_TI_TEMPLATE (function)
|
||||
== DECL_TI_TEMPLATE (fndecl))))
|
||||
return fndecl;
|
||||
|
||||
if (is_member_template (fndecl))
|
||||
/* This function might be an instantiation
|
||||
or specialization of fndecl. */
|
||||
templates =
|
||||
scratch_tree_cons (NULL_TREE, fndecl, templates);
|
||||
}
|
||||
}
|
||||
break; /* loser */
|
||||
}
|
||||
else if (TREE_CODE (OVL_CURRENT (fndecl)) == TEMPLATE_DECL
|
||||
&& DECL_CONV_FN_P (OVL_CURRENT (fndecl))
|
||||
&& DECL_CONV_FN_P (function))
|
||||
/* The method in the class is a member template
|
||||
conversion operator. We are declaring another
|
||||
conversion operator. It is possible that even though
|
||||
the names don't match, there is some specialization
|
||||
occurring. */
|
||||
templates =
|
||||
scratch_tree_cons (NULL_TREE, fndecl, templates);
|
||||
}
|
||||
}
|
||||
|
||||
if (templates)
|
||||
/* This function might be an instantiation or a specialization.
|
||||
We should verify that this is possible. For now, we simply
|
||||
return NULL_TREE, which lets the caller know that this function
|
||||
is new, but we don't print an error message. */
|
||||
return NULL_TREE;
|
||||
|
||||
if (methods != end && *methods)
|
||||
{
|
||||
tree fndecl = *methods;
|
||||
|
332
gcc/cp/pt.c
332
gcc/cp/pt.c
@ -906,13 +906,19 @@ print_candidates (fns)
|
||||
|
||||
/* Returns the template (one of the functions given by TEMPLATE_ID)
|
||||
which can be specialized to match the indicated DECL with the
|
||||
explicit template args given in TEMPLATE_ID. If
|
||||
NEED_MEMBER_TEMPLATE is true the function is a specialization of a
|
||||
member template. The template args (those explicitly specified and
|
||||
those deduced) are output in a newly created vector *TARGS_OUT. If
|
||||
it is impossible to determine the result, an error message is
|
||||
issued, unless COMPLAIN is 0. The DECL may be NULL_TREE if none is
|
||||
available. */
|
||||
explicit template args given in TEMPLATE_ID. The DECL may be
|
||||
NULL_TREE if none is available. In that case, the functions in
|
||||
TEMPLATE_ID are non-members.
|
||||
|
||||
If NEED_MEMBER_TEMPLATE is non-zero the function is known to be a
|
||||
specialization of a member template.
|
||||
|
||||
The template args (those explicitly specified and those deduced)
|
||||
are output in a newly created vector *TARGS_OUT.
|
||||
|
||||
If it is impossible to determine the result, an error message is
|
||||
issued, unless COMPLAIN is 0. In any case, error_mark_node is
|
||||
returned to indicate failure. */
|
||||
|
||||
tree
|
||||
determine_specialization (template_id, decl, targs_out,
|
||||
@ -924,9 +930,12 @@ determine_specialization (template_id, decl, targs_out,
|
||||
int need_member_template;
|
||||
int complain;
|
||||
{
|
||||
tree fns, targs_in;
|
||||
tree templates = NULL_TREE;
|
||||
tree fn;
|
||||
tree fns;
|
||||
tree targs;
|
||||
tree explicit_targs;
|
||||
tree candidates = NULL_TREE;
|
||||
tree templates = NULL_TREE;
|
||||
|
||||
*targs_out = NULL_TREE;
|
||||
|
||||
@ -934,7 +943,7 @@ determine_specialization (template_id, decl, targs_out,
|
||||
return error_mark_node;
|
||||
|
||||
fns = TREE_OPERAND (template_id, 0);
|
||||
targs_in = TREE_OPERAND (template_id, 1);
|
||||
explicit_targs = TREE_OPERAND (template_id, 1);
|
||||
|
||||
if (fns == error_mark_node)
|
||||
return error_mark_node;
|
||||
@ -948,25 +957,58 @@ determine_specialization (template_id, decl, targs_out,
|
||||
tree tmpl;
|
||||
|
||||
fn = OVL_CURRENT (fns);
|
||||
if (!need_member_template
|
||||
&& TREE_CODE (fn) == FUNCTION_DECL
|
||||
&& DECL_FUNCTION_MEMBER_P (fn)
|
||||
&& DECL_USE_TEMPLATE (fn)
|
||||
&& DECL_TI_TEMPLATE (fn))
|
||||
/* We can get here when processing something like:
|
||||
template <class T> class X { void f(); }
|
||||
template <> void X<int>::f() {}
|
||||
We're specializing a member function, but not a member
|
||||
template. */
|
||||
tmpl = DECL_TI_TEMPLATE (fn);
|
||||
else if (TREE_CODE (fn) != TEMPLATE_DECL
|
||||
|| (need_member_template && !is_member_template (fn)))
|
||||
|
||||
if (TREE_CODE (fn) == TEMPLATE_DECL)
|
||||
/* DECL might be a specialization of FN. */
|
||||
tmpl = fn;
|
||||
else if (need_member_template)
|
||||
/* FN is an ordinary member function, and we need a
|
||||
specialization of a member template. */
|
||||
continue;
|
||||
else if (TREE_CODE (fn) != FUNCTION_DECL)
|
||||
/* We can get IDENTIFIER_NODEs here in certain erroneous
|
||||
cases. */
|
||||
continue;
|
||||
else if (!DECL_FUNCTION_MEMBER_P (fn))
|
||||
/* This is just an ordinary non-member function. Nothing can
|
||||
be a specialization of that. */
|
||||
continue;
|
||||
else if (!decl)
|
||||
/* When there's no DECL to match, we know we're looking for
|
||||
non-members. */
|
||||
continue;
|
||||
else
|
||||
tmpl = fn;
|
||||
{
|
||||
tree decl_arg_types;
|
||||
|
||||
if (list_length (targs_in) > DECL_NTPARMS (tmpl))
|
||||
continue;
|
||||
/* This is an ordinary member function. However, since
|
||||
we're here, we can assume it's enclosing class is a
|
||||
template class. For example,
|
||||
|
||||
template <typename T> struct S { void f(); };
|
||||
template <> void S<int>::f() {}
|
||||
|
||||
Here, S<int>::f is a non-template, but S<int> is a
|
||||
template class. If FN has the same type as DECL, we
|
||||
might be in business. */
|
||||
if (!same_type_p (TREE_TYPE (TREE_TYPE (decl)),
|
||||
TREE_TYPE (TREE_TYPE (fn))))
|
||||
/* The return types differ. */
|
||||
continue;
|
||||
|
||||
/* Adjust the type of DECL in case FN is a static member. */
|
||||
decl_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl));
|
||||
if (DECL_STATIC_FUNCTION_P (fn)
|
||||
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
|
||||
decl_arg_types = TREE_CHAIN (decl_arg_types);
|
||||
|
||||
if (compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
|
||||
decl_arg_types))
|
||||
/* They match! */
|
||||
candidates = tree_cons (NULL_TREE, fn, candidates);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (decl == NULL_TREE)
|
||||
{
|
||||
@ -974,70 +1016,107 @@ determine_specialization (template_id, decl, targs_out,
|
||||
make sense and there aren't any undeducible parms. It's OK if
|
||||
not all the parms are specified; they might be deduced
|
||||
later. */
|
||||
tree targs = get_bindings_overload (tmpl, DECL_RESULT (tmpl),
|
||||
targs_in);
|
||||
|
||||
if (targs)
|
||||
/* Unification was successful. */
|
||||
templates = scratch_tree_cons (targs, tmpl, templates);
|
||||
targs = get_bindings_overload (tmpl, DECL_RESULT (tmpl),
|
||||
explicit_targs);
|
||||
if (uses_template_parms (targs))
|
||||
/* We couldn't deduce all the arguments. */
|
||||
continue;
|
||||
}
|
||||
else
|
||||
templates = scratch_tree_cons (NULL_TREE, tmpl, templates);
|
||||
}
|
||||
|
||||
if (decl != NULL_TREE)
|
||||
{
|
||||
tree tmpl = most_specialized (templates, decl, targs_in);
|
||||
tree inner_args;
|
||||
tree tmpl_args;
|
||||
/* See whether this function might be a specialization of this
|
||||
template. */
|
||||
targs = get_bindings (tmpl, decl, explicit_targs);
|
||||
|
||||
if (tmpl == error_mark_node)
|
||||
goto ambiguous;
|
||||
else if (tmpl == NULL_TREE)
|
||||
goto no_match;
|
||||
if (!targs)
|
||||
/* Wwe cannot deduce template arguments that when used to
|
||||
specialize TMPL will produce DECL. */
|
||||
continue;
|
||||
|
||||
inner_args = get_bindings (tmpl, decl, targs_in);
|
||||
tmpl_args = DECL_TI_ARGS (DECL_RESULT (tmpl));
|
||||
if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (tmpl_args))
|
||||
{
|
||||
*targs_out = copy_node (tmpl_args);
|
||||
SET_TMPL_ARGS_LEVEL (*targs_out,
|
||||
TMPL_ARGS_DEPTH (*targs_out),
|
||||
inner_args);
|
||||
}
|
||||
else
|
||||
*targs_out = inner_args;
|
||||
|
||||
return tmpl;
|
||||
/* Save this template, and the arguments deduced. */
|
||||
templates = scratch_tree_cons (targs, tmpl, templates);
|
||||
}
|
||||
|
||||
if (templates == NULL_TREE)
|
||||
if (decl && templates && TREE_CHAIN (templates))
|
||||
{
|
||||
/* We have:
|
||||
|
||||
[temp.expl.spec]
|
||||
|
||||
It is possible for a specialization with a given function
|
||||
signature to be instantiated from more than one function
|
||||
template. In such cases, explicit specification of the
|
||||
template arguments must be used to uniquely identify the
|
||||
function template specialization being specialized.
|
||||
|
||||
Note that here, there's no suggestion that we're supposed to
|
||||
determine which of the candidate templates is most
|
||||
specialized. However, we, also have:
|
||||
|
||||
[temp.func.order]
|
||||
|
||||
Partial ordering of overloaded function template
|
||||
declarations is used in the following contexts to select
|
||||
the function template to which a function template
|
||||
specialization refers:
|
||||
|
||||
-- when an explicit specialization refers to a function
|
||||
template.
|
||||
|
||||
So, we do use the partial ordering rules, at least for now.
|
||||
This extension can only serve to make illegal programs legal,
|
||||
so it's safe. And, there is strong anecdotal evidence that
|
||||
the committee intended the partial ordering rules to apply;
|
||||
the EDG front-end has that behavior, and John Spicer claims
|
||||
that the committee simply forgot to delete the wording in
|
||||
[temp.expl.spec]. */
|
||||
tree tmpl = most_specialized (templates, decl, explicit_targs);
|
||||
if (tmpl && tmpl != error_mark_node)
|
||||
{
|
||||
targs = get_bindings (tmpl, decl, explicit_targs);
|
||||
templates = scratch_tree_cons (targs, tmpl, NULL_TREE);
|
||||
}
|
||||
}
|
||||
|
||||
if (templates == NULL_TREE && candidates == NULL_TREE)
|
||||
{
|
||||
no_match:
|
||||
if (complain)
|
||||
{
|
||||
cp_error_at ("template-id `%D' for `%+D' does not match any template declaration",
|
||||
template_id, decl);
|
||||
return error_mark_node;
|
||||
}
|
||||
return NULL_TREE;
|
||||
cp_error_at ("template-id `%D' for `%+D' does not match any template declaration",
|
||||
template_id, decl);
|
||||
return error_mark_node;
|
||||
}
|
||||
else if (TREE_CHAIN (templates) != NULL_TREE
|
||||
|| uses_template_parms (TREE_PURPOSE (templates)))
|
||||
else if ((templates && TREE_CHAIN (templates))
|
||||
|| (candidates && TREE_CHAIN (candidates)))
|
||||
{
|
||||
ambiguous:
|
||||
if (complain)
|
||||
{
|
||||
cp_error_at ("ambiguous template specialization `%D' for `%+D'",
|
||||
template_id, decl);
|
||||
print_candidates (templates);
|
||||
return error_mark_node;
|
||||
chainon (candidates, templates);
|
||||
print_candidates (candidates);
|
||||
}
|
||||
return NULL_TREE;
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
/* We have one, and exactly one, match. */
|
||||
*targs_out = TREE_PURPOSE (templates);
|
||||
if (candidates)
|
||||
{
|
||||
/* It was a specialization of an ordinary member function in a
|
||||
template class. */
|
||||
*targs_out = copy_node (DECL_TI_ARGS (TREE_VALUE (candidates)));
|
||||
return DECL_TI_TEMPLATE (TREE_VALUE (candidates));
|
||||
}
|
||||
|
||||
/* It was a specialization of a template. */
|
||||
targs = DECL_TI_ARGS (DECL_RESULT (TREE_VALUE (templates)));
|
||||
if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (targs))
|
||||
{
|
||||
*targs_out = copy_node (targs);
|
||||
SET_TMPL_ARGS_LEVEL (*targs_out,
|
||||
TMPL_ARGS_DEPTH (*targs_out),
|
||||
TREE_PURPOSE (templates));
|
||||
}
|
||||
else
|
||||
*targs_out = TREE_PURPOSE (templates);
|
||||
return TREE_VALUE (templates);
|
||||
}
|
||||
|
||||
@ -1047,7 +1126,9 @@ determine_specialization (template_id, decl, targs_out,
|
||||
instantiation at this point.
|
||||
|
||||
Returns DECL, or an equivalent declaration that should be used
|
||||
instead.
|
||||
instead if all goes well. Issues an error message if something is
|
||||
amiss. Returns error_mark_node if the error is not easily
|
||||
recoverable.
|
||||
|
||||
FLAGS is a bitmask consisting of the following flags:
|
||||
|
||||
@ -1101,10 +1182,9 @@ check_explicit_specialization (declarator, decl, template_count, flags)
|
||||
/* There were more template headers than qualifying template
|
||||
classes. */
|
||||
if (template_header_count - template_count > 1)
|
||||
/* There shouldn't be that many template parameter
|
||||
lists. There can be at most one parameter list for
|
||||
every qualifying class, plus one for the function
|
||||
itself. */
|
||||
/* There shouldn't be that many template parameter lists.
|
||||
There can be at most one parameter list for every
|
||||
qualifying class, plus one for the function itself. */
|
||||
cp_error ("too many template parameter lists in declaration of `%D'", decl);
|
||||
|
||||
SET_DECL_TEMPLATE_SPECIALIZATION (decl);
|
||||
@ -1284,8 +1364,9 @@ check_explicit_specialization (declarator, decl, template_count, flags)
|
||||
/* Find the list of functions in ctype that have the same
|
||||
name as the declared function. */
|
||||
tree name = TREE_OPERAND (declarator, 0);
|
||||
tree fns;
|
||||
|
||||
tree fns = NULL_TREE;
|
||||
int idx;
|
||||
|
||||
if (name == constructor_name (ctype)
|
||||
|| name == constructor_name_full (ctype))
|
||||
{
|
||||
@ -1303,21 +1384,52 @@ check_explicit_specialization (declarator, decl, template_count, flags)
|
||||
|
||||
Similar language is found in [temp.explicit]. */
|
||||
cp_error ("specialization of implicitly-declared special member function");
|
||||
|
||||
return decl;
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
name = is_constructor ? ctor_identifier : dtor_identifier;
|
||||
}
|
||||
|
||||
fns = lookup_fnfields (TYPE_BINFO (ctype), name, 1);
|
||||
|
||||
if (!IDENTIFIER_TYPENAME_P (name))
|
||||
{
|
||||
idx = lookup_fnfields_1 (ctype, name);
|
||||
if (idx >= 0)
|
||||
fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (ctype), idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
tree methods;
|
||||
|
||||
/* For a type-conversion operator, we cannot do a
|
||||
name-based lookup. We might be looking for `operator
|
||||
int' which will be a specialization of `operator T'.
|
||||
So, we find *all* the conversion operators, and then
|
||||
select from them. */
|
||||
fns = NULL_TREE;
|
||||
|
||||
methods = CLASSTYPE_METHOD_VEC (ctype);
|
||||
if (methods)
|
||||
for (idx = 2; idx < TREE_VEC_LENGTH (methods); ++idx)
|
||||
{
|
||||
tree ovl = TREE_VEC_ELT (methods, idx);
|
||||
|
||||
if (!ovl || !DECL_CONV_FN_P (OVL_CURRENT (ovl)))
|
||||
/* There are no more conversion functions. */
|
||||
break;
|
||||
|
||||
/* Glue all these conversion functions together
|
||||
with those we already have. */
|
||||
for (; ovl; ovl = OVL_NEXT (ovl))
|
||||
fns = ovl_cons (OVL_CURRENT (ovl), fns);
|
||||
}
|
||||
}
|
||||
|
||||
if (fns == NULL_TREE)
|
||||
{
|
||||
cp_error ("no member function `%s' declared in `%T'",
|
||||
IDENTIFIER_POINTER (name),
|
||||
ctype);
|
||||
return decl;
|
||||
return error_mark_node;
|
||||
}
|
||||
else
|
||||
TREE_OPERAND (declarator, 0) = fns;
|
||||
@ -1336,7 +1448,11 @@ check_explicit_specialization (declarator, decl, template_count, flags)
|
||||
member_specialization,
|
||||
1);
|
||||
|
||||
if (tmpl && tmpl != error_mark_node)
|
||||
if (!tmpl || tmpl == error_mark_node)
|
||||
/* We couldn't figure out what this declaration was
|
||||
specializing. */
|
||||
return error_mark_node;
|
||||
else
|
||||
{
|
||||
gen_tmpl = most_general_template (tmpl);
|
||||
|
||||
@ -1390,8 +1506,6 @@ check_explicit_specialization (declarator, decl, template_count, flags)
|
||||
/* Register this specialization so that we can find it
|
||||
again. */
|
||||
decl = register_specialization (decl, gen_tmpl, targs);
|
||||
|
||||
return decl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7123,17 +7237,13 @@ overload_template_name (type)
|
||||
[temp.expl.spec], or when taking the address of a function
|
||||
template, as in [temp.deduct.funcaddr].
|
||||
|
||||
The EXTRA_FN_ARG, if any, is the type of an additional
|
||||
parameter to be added to the beginning of FN's parameter list.
|
||||
|
||||
The other arguments are as for type_unification. */
|
||||
|
||||
int
|
||||
fn_type_unification (fn, explicit_targs, targs, args, return_type,
|
||||
strict, extra_fn_arg)
|
||||
strict)
|
||||
tree fn, explicit_targs, targs, args, return_type;
|
||||
unification_kind_t strict;
|
||||
tree extra_fn_arg;
|
||||
{
|
||||
tree parms;
|
||||
tree fntype;
|
||||
@ -7174,11 +7284,6 @@ fn_type_unification (fn, explicit_targs, targs, args, return_type,
|
||||
if (fntype == error_mark_node)
|
||||
return 1;
|
||||
|
||||
extra_fn_arg = tsubst (extra_fn_arg, converted_args,
|
||||
/*complain=*/0, NULL_TREE);
|
||||
if (extra_fn_arg == error_mark_node)
|
||||
return 1;
|
||||
|
||||
/* Place the explicitly specified arguments in TARGS. */
|
||||
for (i = 0; i < TREE_VEC_LENGTH (targs); i++)
|
||||
TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (converted_args, i);
|
||||
@ -7195,9 +7300,6 @@ fn_type_unification (fn, explicit_targs, targs, args, return_type,
|
||||
args = scratch_tree_cons (NULL_TREE, return_type, args);
|
||||
}
|
||||
|
||||
if (extra_fn_arg != NULL_TREE)
|
||||
parms = scratch_tree_cons (NULL_TREE, extra_fn_arg, parms);
|
||||
|
||||
/* We allow incomplete unification without an error message here
|
||||
because the standard doesn't seem to explicitly prohibit it. Our
|
||||
callers must be ready to deal with unification failures in any
|
||||
@ -8355,9 +8457,8 @@ get_bindings_real (fn, decl, explicit_args, check_rettype)
|
||||
{
|
||||
int ntparms = DECL_NTPARMS (fn);
|
||||
tree targs = make_scratch_vec (ntparms);
|
||||
tree decl_arg_types;
|
||||
tree extra_fn_arg = NULL_TREE;
|
||||
tree decl_type;
|
||||
tree decl_arg_types;
|
||||
int i;
|
||||
|
||||
/* Substitute the explicit template arguments into the type of DECL.
|
||||
@ -8389,32 +8490,17 @@ get_bindings_real (fn, decl, explicit_args, check_rettype)
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* If FN is a static member function, adjust the type of DECL
|
||||
appropriately. */
|
||||
decl_arg_types = TYPE_ARG_TYPES (decl_type);
|
||||
|
||||
if (DECL_STATIC_FUNCTION_P (fn)
|
||||
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
|
||||
{
|
||||
/* Sometimes we are trying to figure out what's being
|
||||
specialized by a declaration that looks like a method, and it
|
||||
turns out to be a static member function. */
|
||||
if (CLASSTYPE_TEMPLATE_INFO (DECL_REAL_CONTEXT (fn))
|
||||
&& !is_member_template (fn))
|
||||
/* The natural thing to do here seems to be to remove the
|
||||
spurious `this' parameter from the DECL, but that prevents
|
||||
unification from making use of the class type. So,
|
||||
instead, we have fn_type_unification add to the parameters
|
||||
for FN. */
|
||||
extra_fn_arg = build_pointer_type (DECL_REAL_CONTEXT (fn));
|
||||
else
|
||||
/* In this case, though, adding the extra_fn_arg can confuse
|
||||
things, so we remove from decl_arg_types instead. */
|
||||
decl_arg_types = TREE_CHAIN (decl_arg_types);
|
||||
}
|
||||
decl_arg_types = TREE_CHAIN (decl_arg_types);
|
||||
|
||||
i = fn_type_unification (fn, explicit_args, targs,
|
||||
decl_arg_types, TREE_TYPE (decl_type),
|
||||
DEDUCE_EXACT,
|
||||
extra_fn_arg);
|
||||
decl_arg_types,
|
||||
TREE_TYPE (decl_type),
|
||||
DEDUCE_EXACT);
|
||||
|
||||
if (i != 0)
|
||||
return NULL_TREE;
|
||||
|
@ -81,7 +81,6 @@ static tree get_vbase_1 PROTO((tree, tree, unsigned int *));
|
||||
static tree convert_pointer_to_vbase PROTO((tree, tree));
|
||||
static tree lookup_field_1 PROTO((tree, tree));
|
||||
static tree convert_pointer_to_single_level PROTO((tree, tree));
|
||||
static int lookup_fnfields_1 PROTO((tree, tree));
|
||||
static int lookup_fnfields_here PROTO((tree, tree));
|
||||
static int is_subobject_of_p PROTO((tree, tree));
|
||||
static int hides PROTO((tree, tree));
|
||||
@ -1284,7 +1283,7 @@ lookup_nested_field (name, complain)
|
||||
/* TYPE is a class type. Return the index of the fields within
|
||||
the method vector with name NAME, or -1 is no such field exists. */
|
||||
|
||||
static int
|
||||
int
|
||||
lookup_fnfields_1 (type, name)
|
||||
tree type, name;
|
||||
{
|
||||
|
@ -1185,15 +1185,12 @@ common_base_type (tt1, tt2)
|
||||
|
||||
/* Subroutines of `comptypes'. */
|
||||
|
||||
/* Return 1 if two parameter type lists PARMS1 and PARMS2
|
||||
are equivalent in the sense that functions with those parameter types
|
||||
can have equivalent types.
|
||||
If either list is empty, we win.
|
||||
Otherwise, the two lists must be equivalent, element by element.
|
||||
/* Return 1 if two parameter type lists PARMS1 and PARMS2 are
|
||||
equivalent in the sense that functions with those parameter types
|
||||
can have equivalent types. The two lists must be equivalent,
|
||||
element by element.
|
||||
|
||||
C++: See comment above about TYPE1, TYPE2.
|
||||
|
||||
STRICT is no longer used. */
|
||||
C++: See comment above about TYPE1, TYPE2. */
|
||||
|
||||
int
|
||||
compparms (parms1, parms2)
|
||||
@ -4559,30 +4556,7 @@ build_unary_op (code, xarg, noconvert)
|
||||
return build1 (ADDR_EXPR, unknown_type_node, arg);
|
||||
}
|
||||
|
||||
if (TREE_CODE (arg) == TEMPLATE_ID_EXPR)
|
||||
{
|
||||
tree targs;
|
||||
tree fn;
|
||||
|
||||
/* We don't require a match here; it's possible that the
|
||||
context (like a cast to a particular type) will resolve
|
||||
the particular choice of template. */
|
||||
fn = determine_specialization (arg,
|
||||
NULL_TREE,
|
||||
&targs,
|
||||
0,
|
||||
0);
|
||||
|
||||
if (fn)
|
||||
{
|
||||
fn = instantiate_template (fn, targs);
|
||||
mark_addressable (fn);
|
||||
return build_unary_op (ADDR_EXPR, fn, 0);
|
||||
}
|
||||
|
||||
return build1 (ADDR_EXPR, unknown_type_node, arg);
|
||||
}
|
||||
else if (type_unknown_p (arg))
|
||||
if (type_unknown_p (arg))
|
||||
return build1 (ADDR_EXPR, unknown_type_node, arg);
|
||||
|
||||
/* Handle complex lvalues (when permitted)
|
||||
|
9
gcc/testsuite/g++.old-deja/g++.other/redecl2.C
Normal file
9
gcc/testsuite/g++.old-deja/g++.other/redecl2.C
Normal file
@ -0,0 +1,9 @@
|
||||
// Build don't link:
|
||||
|
||||
struct S {
|
||||
S(int);
|
||||
S(int); // ERROR - already declared
|
||||
|
||||
~S();
|
||||
~S(); // ERROR - already declared
|
||||
};
|
@ -5,5 +5,5 @@ void foo(T t) {}
|
||||
|
||||
void bar()
|
||||
{
|
||||
&foo<double>;
|
||||
(void (*)(double)) &foo<double>;
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ void foo(T t) {}
|
||||
|
||||
void bar()
|
||||
{
|
||||
(void (*)(int)) &foo<double>;
|
||||
(void (*)(int)) (void (*)(double)) &foo<double>;
|
||||
}
|
||||
|
@ -8,5 +8,5 @@ int foo(int i) { return 0; }
|
||||
|
||||
int main()
|
||||
{
|
||||
&foo<int>;
|
||||
(int (*)(int)) &foo<int>;
|
||||
}
|
||||
|
@ -8,5 +8,5 @@ void foo(int i) {}
|
||||
|
||||
int main()
|
||||
{
|
||||
&foo<int>;
|
||||
(void (*)(int)) &foo<int>;
|
||||
}
|
||||
|
@ -8,5 +8,5 @@ int foo(int i) { return 0; }
|
||||
|
||||
int main()
|
||||
{
|
||||
return (*&foo<int>)(3);
|
||||
return (*((int (*)(int)) &foo<int>))(3);
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ void foo(T, T*);
|
||||
void bar()
|
||||
{
|
||||
double d;
|
||||
(*((void (*)(int, double*)) &foo<int>))(3, &d);
|
||||
(*((void (*)(int, double*)) (void (*)(int, int*)) &foo<int>))(3, &d);
|
||||
}
|
||||
|
31
gcc/testsuite/g++.old-deja/g++.pt/spec29.C
Normal file
31
gcc/testsuite/g++.old-deja/g++.pt/spec29.C
Normal file
@ -0,0 +1,31 @@
|
||||
char c;
|
||||
|
||||
struct S {
|
||||
template <typename T>
|
||||
operator T*();
|
||||
|
||||
template <typename T>
|
||||
operator T();
|
||||
};
|
||||
|
||||
template <>
|
||||
S::operator int()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
template <>
|
||||
S::operator char*()
|
||||
{
|
||||
return &c;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
S s;
|
||||
int i = s;
|
||||
char* cp = s;
|
||||
|
||||
if (i != 2 || cp != &c)
|
||||
return 1;
|
||||
}
|
41
gcc/testsuite/g++.old-deja/g++.pt/spec30.C
Normal file
41
gcc/testsuite/g++.old-deja/g++.pt/spec30.C
Normal file
@ -0,0 +1,41 @@
|
||||
#include <cstddef>
|
||||
|
||||
template <class T>
|
||||
struct S {
|
||||
void *operator new (size_t);
|
||||
void *operator new (size_t, int);
|
||||
void operator delete (void*);
|
||||
};
|
||||
|
||||
static void* s[2];
|
||||
|
||||
template <>
|
||||
void* S<int>::operator new (size_t b)
|
||||
{
|
||||
s[0] = ::operator new(b);
|
||||
return s[0];
|
||||
}
|
||||
|
||||
template <>
|
||||
void* S<int>::operator new (size_t b, int)
|
||||
{
|
||||
s[1] = ::operator new(b);
|
||||
return s[1];
|
||||
}
|
||||
|
||||
template <>
|
||||
void S<int>::operator delete (void*)
|
||||
{
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
S<int>* s1 = new S<int>;
|
||||
S<int>* s2 = new(3) S<int>;
|
||||
|
||||
if (s1 != s[0] || s2 != s[1])
|
||||
return 1;
|
||||
|
||||
delete s1;
|
||||
delete s2;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user