decl2.c (build_anon_union_vars): Don't crash on empty sub-unions.

1998-05-25  Mark Mitchell  <mark@markmitchell.com>
	* decl2.c (build_anon_union_vars): Don't crash on empty sub-unions.
	* cp-tree.h (processing_template_parmlist): Declare.
	* decl.c (pushtag): Don't call push_template_decl when we
	shouldn't.
	* pt.c (processing_template_parmlist): New variable.
	(TMPL_ARGS_HAVE_MULTIPLE_LEVELS): New macro.
	(complete_template_args): Use it.
	(add_to_template_args): Likewise.
	(innermost_args): Likewise.
	(tsubst): Likewise.
	(begin_template_parm_list): Use processing_template_parmlist.
	(end_template_parm_list): Likewise.
	* cp-tree.h (ANON_UNION_TYPE_P): New macro.
	* decl.c (grokdeclarator): Use it.
	* decl2.c (grok_x_components): Likewise.
	* init.c (initializing_context): Likewise.
	* method.c (do_build_copy_constructor): Likewise.
	(do_build_assign_ref): Likewise.
	* search.c (compute_access): Likewise.
	* typeck.c (build_component_ref): Likewise.
	* decl.c (grokdeclarator): Don't give a cv-qualified version of an
	unnamed type a typedef name "for linkage purposes".
	* pt.c (lookup_template_class): Don't look at
	IDENTIFIER_CLASS_VALUE when there's no current_class_type.
	* method.c (build_overload_int): Handle error cases gracefully.
	* pt.c (instantiate_decl): Handle static member variables
	correctly.
	* pt.c (tsubst): Use the tsubst'd type when producing new
	TEMPLATE_PARM_INDEX nodes.

From-SVN: r20045
This commit is contained in:
Mark Mitchell 1998-05-25 10:28:16 +00:00 committed by Mark Mitchell
parent c21f27a762
commit 67ffc8124f
14 changed files with 215 additions and 52 deletions

View File

@ -1,3 +1,42 @@
1998-05-25 Mark Mitchell <mark@markmitchell.com>
* decl2.c (build_anon_union_vars): Don't crash on empty sub-unions.
* cp-tree.h (processing_template_parmlist): Declare.
* decl.c (pushtag): Don't call push_template_decl when we
shouldn't.
* pt.c (processing_template_parmlist): New variable.
(TMPL_ARGS_HAVE_MULTIPLE_LEVELS): New macro.
(complete_template_args): Use it.
(add_to_template_args): Likewise.
(innermost_args): Likewise.
(tsubst): Likewise.
(begin_template_parm_list): Use processing_template_parmlist.
(end_template_parm_list): Likewise.
* cp-tree.h (ANON_UNION_TYPE_P): New macro.
* decl.c (grokdeclarator): Use it.
* decl2.c (grok_x_components): Likewise.
* init.c (initializing_context): Likewise.
* method.c (do_build_copy_constructor): Likewise.
(do_build_assign_ref): Likewise.
* search.c (compute_access): Likewise.
* typeck.c (build_component_ref): Likewise.
* decl.c (grokdeclarator): Don't give a cv-qualified version of an
unnamed type a typedef name "for linkage purposes".
* pt.c (lookup_template_class): Don't look at
IDENTIFIER_CLASS_VALUE when there's no current_class_type.
* method.c (build_overload_int): Handle error cases gracefully.
* pt.c (instantiate_decl): Handle static member variables
correctly.
* pt.c (tsubst): Use the tsubst'd type when producing new
TEMPLATE_PARM_INDEX nodes.
1998-05-24 Mark Mitchell <mark@markmitchell.com> 1998-05-24 Mark Mitchell <mark@markmitchell.com>
* tree.c (cp_tree_equal): Handle pointers to member functions. * tree.c (cp_tree_equal): Handle pointers to member functions.

View File

@ -1452,6 +1452,11 @@ extern int flag_new_for_scope;
#define ANON_UNION_P(NODE) (DECL_NAME (NODE) == 0) #define ANON_UNION_P(NODE) (DECL_NAME (NODE) == 0)
/* Nonzero if TYPE is an anonymous union type. */
#define ANON_UNION_TYPE_P(TYPE) \
(TREE_CODE (TYPE) == UNION_TYPE \
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (TYPE)))
#define UNKNOWN_TYPE LANG_TYPE #define UNKNOWN_TYPE LANG_TYPE
/* Define fields and accessors for nodes representing declared names. */ /* Define fields and accessors for nodes representing declared names. */
@ -2644,6 +2649,7 @@ extern int comp_template_args PROTO((tree, tree));
extern int processing_specialization; extern int processing_specialization;
extern int processing_explicit_instantiation; extern int processing_explicit_instantiation;
extern int processing_template_parmlist;
/* in repo.c */ /* in repo.c */
extern void repo_template_used PROTO((tree)); extern void repo_template_used PROTO((tree));

View File

@ -2291,9 +2291,15 @@ pushtag (name, type, globalize)
friend class S2; friend class S2;
}; };
declares S2 to be at global scope. */ declares S2 to be at global scope. We must be
|| (processing_template_decl > careful, however, of the following case:
template_class_depth (current_class_type))))
template <class A*> struct S;
which declares a non-template class `A'. */
|| (!processing_template_parmlist
&& (processing_template_decl >
template_class_depth (current_class_type)))))
{ {
d = push_template_decl_real (d, globalize); d = push_template_decl_real (d, globalize);
/* If the current binding level is the binding level for /* If the current binding level is the binding level for
@ -8750,8 +8756,7 @@ grokdeclarator (declarator, declspecs, decl_context, initialized, attrlist)
/* Static anonymous unions are dealt with here. */ /* Static anonymous unions are dealt with here. */
if (staticp && decl_context == TYPENAME if (staticp && decl_context == TYPENAME
&& TREE_CODE (declspecs) == TREE_LIST && TREE_CODE (declspecs) == TREE_LIST
&& TREE_CODE (TREE_VALUE (declspecs)) == UNION_TYPE && ANON_UNION_TYPE_P (TREE_VALUE (declspecs)))
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (TREE_VALUE (declspecs))))
decl_context = FIELD; decl_context = FIELD;
/* Give error if `const,' `volatile,' `inline,' `friend,' or `virtual' /* Give error if `const,' `volatile,' `inline,' `friend,' or `virtual'
@ -9659,6 +9664,7 @@ grokdeclarator (declarator, declspecs, decl_context, initialized, attrlist)
refer to it, so nothing needs know about the name change. refer to it, so nothing needs know about the name change.
The TYPE_NAME field was filled in by build_struct_xref. */ The TYPE_NAME field was filled in by build_struct_xref. */
if (type != error_mark_node if (type != error_mark_node
&& !TYPE_READONLY (type) && !TYPE_VOLATILE (type)
&& TYPE_NAME (type) && TYPE_NAME (type)
&& TREE_CODE (TYPE_NAME (type)) == TYPE_DECL && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (type))) && ANON_AGGRNAME_P (TYPE_IDENTIFIER (type)))

View File

@ -865,8 +865,7 @@ grok_x_components (specs, components)
{ {
case VAR_DECL: case VAR_DECL:
/* Static anonymous unions come out as VAR_DECLs. */ /* Static anonymous unions come out as VAR_DECLs. */
if (TREE_CODE (TREE_TYPE (t)) == UNION_TYPE if (ANON_UNION_TYPE_P (TREE_TYPE (t)))
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (TREE_TYPE (t))))
return t; return t;
/* We return SPECS here, because in the parser it was ending /* We return SPECS here, because in the parser it was ending
@ -904,8 +903,7 @@ grok_x_components (specs, components)
tcode = enum_type_node; tcode = enum_type_node;
t = xref_tag (tcode, TYPE_IDENTIFIER (t), NULL_TREE, 0); t = xref_tag (tcode, TYPE_IDENTIFIER (t), NULL_TREE, 0);
if (TREE_CODE (t) == UNION_TYPE if (ANON_UNION_TYPE_P (t))
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (t)))
{ {
/* See also shadow_tag. */ /* See also shadow_tag. */
@ -2176,7 +2174,11 @@ build_anon_union_vars (anon_decl, elems, static_p, external_p)
if (DECL_NAME (field) == NULL_TREE if (DECL_NAME (field) == NULL_TREE
&& TREE_CODE (TREE_TYPE (field)) == UNION_TYPE) && TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
{
decl = build_anon_union_vars (field, elems, static_p, external_p); decl = build_anon_union_vars (field, elems, static_p, external_p);
if (!decl)
continue;
}
else else
{ {
decl = build_decl (VAR_DECL, DECL_NAME (field), TREE_TYPE (field)); decl = build_decl (VAR_DECL, DECL_NAME (field), TREE_TYPE (field));

View File

@ -866,7 +866,7 @@ initializing_context (field)
/* Anonymous union members can be initialized in the first enclosing /* Anonymous union members can be initialized in the first enclosing
non-anonymous union context. */ non-anonymous union context. */
while (t && ANON_AGGRNAME_P (TYPE_IDENTIFIER (t))) while (t && ANON_UNION_TYPE_P (t))
t = TYPE_CONTEXT (t); t = TYPE_CONTEXT (t);
return t; return t;
} }

View File

@ -515,7 +515,13 @@ build_overload_int (value, in_template)
id = ansi_opname [(int) TREE_CODE (value)]; id = ansi_opname [(int) TREE_CODE (value)];
my_friendly_assert (id != NULL_TREE, 0); my_friendly_assert (id != NULL_TREE, 0);
name = IDENTIFIER_POINTER (id); name = IDENTIFIER_POINTER (id);
my_friendly_assert (name[0] == '_' && name[1] == '_', 0); if (name[0] != '_' || name[1] != '_')
/* On some erroneous inputs, we can get here with VALUE a
LOOKUP_EXPR. In that case, the NAME will be the
identifier for "<invalid operator>". We must survive
this routine in order to issue a sensible error
message, so we fall through to the case below. */
goto bad_value;
for (i = 0; i < operands; ++i) for (i = 0; i < operands; ++i)
{ {
@ -553,6 +559,7 @@ build_overload_int (value, in_template)
This should cause assembler errors we'll notice. */ This should cause assembler errors we'll notice. */
static int n; static int n;
bad_value:
sprintf (digit_buffer, " *%d", n++); sprintf (digit_buffer, " *%d", n++);
OB_PUTCP (digit_buffer); OB_PUTCP (digit_buffer);
} }
@ -2180,8 +2187,7 @@ do_build_copy_constructor (fndecl)
continue; continue;
} }
else if ((t = TREE_TYPE (field)) != NULL_TREE else if ((t = TREE_TYPE (field)) != NULL_TREE
&& TREE_CODE (t) == UNION_TYPE && ANON_UNION_TYPE_P (t)
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (t))
&& TYPE_FIELDS (t) != NULL_TREE) && TYPE_FIELDS (t) != NULL_TREE)
{ {
do do
@ -2190,8 +2196,7 @@ do_build_copy_constructor (fndecl)
field = largest_union_member (t); field = largest_union_member (t);
} }
while ((t = TREE_TYPE (field)) != NULL_TREE while ((t = TREE_TYPE (field)) != NULL_TREE
&& TREE_CODE (t) == UNION_TYPE && ANON_UNION_TYPE_P (t)
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (t))
&& TYPE_FIELDS (t) != NULL_TREE); && TYPE_FIELDS (t) != NULL_TREE);
} }
else else
@ -2290,8 +2295,7 @@ do_build_assign_ref (fndecl)
continue; continue;
} }
else if ((t = TREE_TYPE (field)) != NULL_TREE else if ((t = TREE_TYPE (field)) != NULL_TREE
&& TREE_CODE (t) == UNION_TYPE && ANON_UNION_TYPE_P (t)
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (t))
&& TYPE_FIELDS (t) != NULL_TREE) && TYPE_FIELDS (t) != NULL_TREE)
{ {
do do
@ -2301,8 +2305,7 @@ do_build_assign_ref (fndecl)
field = largest_union_member (t); field = largest_union_member (t);
} }
while ((t = TREE_TYPE (field)) != NULL_TREE while ((t = TREE_TYPE (field)) != NULL_TREE
&& TREE_CODE (t) == UNION_TYPE && ANON_UNION_TYPE_P (t)
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (t))
&& TYPE_FIELDS (t) != NULL_TREE); && TYPE_FIELDS (t) != NULL_TREE);
} }
else else

View File

@ -63,6 +63,7 @@ int minimal_parse_mode;
int processing_specialization; int processing_specialization;
int processing_explicit_instantiation; int processing_explicit_instantiation;
int processing_template_parmlist;
static int template_header_count; static int template_header_count;
static tree saved_trees; static tree saved_trees;
@ -115,6 +116,14 @@ static int check_cv_quals_for_unify PROTO((int, tree, tree));
static tree tsubst_template_arg_vector PROTO((tree, tree)); static tree tsubst_template_arg_vector PROTO((tree, tree));
static void regenerate_decl_from_template PROTO((tree, tree)); static void regenerate_decl_from_template PROTO((tree, tree));
/* Nonzero if ARGVEC contains multiple levels of template arguments. */
#define TMPL_ARGS_HAVE_MULTIPLE_LEVELS(NODE) \
(NODE != NULL_TREE \
&& TREE_CODE (NODE) == TREE_VEC \
&& TREE_VEC_LENGTH (NODE) > 0 \
&& TREE_VEC_ELT (NODE, 0) != NULL_TREE \
&& TREE_CODE (TREE_VEC_ELT (NODE, 0)) == TREE_VEC)
/* Do any processing required when DECL (a member template declaration /* Do any processing required when DECL (a member template declaration
using TEMPLATE_PARAMETERS as its innermost parameter list) is using TEMPLATE_PARAMETERS as its innermost parameter list) is
finished. Returns the TEMPLATE_DECL corresponding to DECL, unless finished. Returns the TEMPLATE_DECL corresponding to DECL, unless
@ -388,7 +397,7 @@ complete_template_args (tmpl, extra_args, unbound_only)
my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 0); my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 0);
my_friendly_assert (TREE_CODE (extra_args) == TREE_VEC, 0); my_friendly_assert (TREE_CODE (extra_args) == TREE_VEC, 0);
if (TREE_CODE (TREE_VEC_ELT (extra_args, 0)) == TREE_VEC) if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (extra_args))
extra_arg_depth = TREE_VEC_LENGTH (extra_args); extra_arg_depth = TREE_VEC_LENGTH (extra_args);
else else
extra_arg_depth = 1; extra_arg_depth = 1;
@ -411,7 +420,7 @@ complete_template_args (tmpl, extra_args, unbound_only)
TEMPLATE_DECL with DECL_TEMPLATE_INFO. DECL_TI_ARGS is TEMPLATE_DECL with DECL_TEMPLATE_INFO. DECL_TI_ARGS is
all the bound template arguments. */ all the bound template arguments. */
args = DECL_TI_ARGS (tmpl); args = DECL_TI_ARGS (tmpl);
if (TREE_CODE (TREE_VEC_ELT (args, 0)) != TREE_VEC) if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
depth = 1; depth = 1;
else else
depth = TREE_VEC_LENGTH (args); depth = TREE_VEC_LENGTH (args);
@ -485,7 +494,7 @@ add_to_template_args (args, extra_args)
{ {
tree new_args; tree new_args;
if (TREE_CODE (TREE_VEC_ELT (args, 0)) != TREE_VEC) if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
{ {
new_args = make_tree_vec (2); new_args = make_tree_vec (2);
TREE_VEC_ELT (new_args, 0) = args; TREE_VEC_ELT (new_args, 0) = args;
@ -529,6 +538,7 @@ begin_template_parm_list ()
pushlevel (0); pushlevel (0);
declare_pseudo_global_level (); declare_pseudo_global_level ();
++processing_template_decl; ++processing_template_decl;
++processing_template_parmlist;
note_template_header (0); note_template_header (0);
} }
@ -1452,6 +1462,8 @@ end_template_parm_list (parms)
for (parm = parms, nparms = 0; parm; parm = TREE_CHAIN (parm), nparms++) for (parm = parms, nparms = 0; parm; parm = TREE_CHAIN (parm), nparms++)
TREE_VEC_ELT (saved_parmlist, nparms) = parm; TREE_VEC_ELT (saved_parmlist, nparms) = parm;
--processing_template_parmlist;
return saved_parmlist; return saved_parmlist;
} }
@ -2858,6 +2870,7 @@ lookup_template_class (d1, arglist, in_decl, context)
{ {
if (context) if (context)
push_decl_namespace (context); push_decl_namespace (context);
if (current_class_type != NULL_TREE)
template = template =
maybe_get_template_decl_from_type_decl maybe_get_template_decl_from_type_decl
(IDENTIFIER_CLASS_VALUE (d1)); (IDENTIFIER_CLASS_VALUE (d1));
@ -3993,7 +4006,7 @@ innermost_args (args, is_spec)
tree args; tree args;
int is_spec; int is_spec;
{ {
if (args != NULL_TREE && TREE_CODE (TREE_VEC_ELT (args, 0)) == TREE_VEC) if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
return TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1 - is_spec); return TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1 - is_spec);
return args; return args;
} }
@ -4189,8 +4202,7 @@ tsubst (t, args, in_decl)
{ {
tree arg = NULL_TREE; tree arg = NULL_TREE;
if (TREE_VEC_ELT (args, 0) != NULL_TREE if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
&& TREE_CODE (TREE_VEC_ELT (args, 0)) == TREE_VEC)
{ {
levels = TREE_VEC_LENGTH (args); levels = TREE_VEC_LENGTH (args);
if (level <= levels) if (level <= levels)
@ -4275,7 +4287,7 @@ tsubst (t, args, in_decl)
break; break;
case TEMPLATE_PARM_INDEX: case TEMPLATE_PARM_INDEX:
r = reduce_template_parm_level (t, TREE_TYPE (t), levels); r = reduce_template_parm_level (t, type, levels);
break; break;
default: default:
@ -6220,14 +6232,14 @@ unify (tparms, targs, parm, arg, strict, explicit_mask)
/* The PARM is not one we're trying to unify. Just check /* The PARM is not one we're trying to unify. Just check
to see if it matches ARG. */ to see if it matches ARG. */
return (TREE_CODE (arg) == TREE_CODE (parm) return (TREE_CODE (arg) == TREE_CODE (parm)
&& cp_tree_equal (parm, arg)) ? 0 : 1; && cp_tree_equal (parm, arg) > 0) ? 0 : 1;
idx = TEMPLATE_PARM_IDX (parm); idx = TEMPLATE_PARM_IDX (parm);
targ = TREE_VEC_ELT (targs, idx); targ = TREE_VEC_ELT (targs, idx);
if (targ) if (targ)
{ {
int i = cp_tree_equal (targ, arg); int i = (cp_tree_equal (targ, arg) > 0);
if (i == 1) if (i == 1)
return 0; return 0;
else if (i == 0) else if (i == 0)
@ -7082,17 +7094,6 @@ instantiate_decl (d)
lineno = DECL_SOURCE_LINE (d); lineno = DECL_SOURCE_LINE (d);
input_filename = DECL_SOURCE_FILE (d); input_filename = DECL_SOURCE_FILE (d);
/* We need to set up DECL_INITIAL regardless of pattern_defined if the
variable is a static const initialized in the class body. */
if (TREE_CODE (d) == VAR_DECL
&& ! DECL_INITIAL (d) && DECL_INITIAL (code_pattern))
{
pushclass (DECL_CONTEXT (d), 2);
DECL_INITIAL (d) = tsubst_expr (DECL_INITIAL (code_pattern), args,
tmpl);
cp_finish_decl (d, DECL_INITIAL (d), NULL_TREE, 0, LOOKUP_NORMAL);
}
if (pattern_defined) if (pattern_defined)
{ {
repo_template_used (d); repo_template_used (d);
@ -7123,11 +7124,17 @@ instantiate_decl (d)
&& ! (TREE_CODE (d) == FUNCTION_DECL && DECL_INLINE (d))) && ! (TREE_CODE (d) == FUNCTION_DECL && DECL_INLINE (d)))
goto out; goto out;
/* Defer all templates except inline functions used in another function. */ if (TREE_CODE (d) == VAR_DECL
if (! pattern_defined && DECL_INITIAL (d) == NULL_TREE
&& DECL_INITIAL (code_pattern) != NULL_TREE)
/* We need to set up DECL_INITIAL regardless of pattern_defined if
the variable is a static const initialized in the class body. */;
else if (! pattern_defined
|| (! (TREE_CODE (d) == FUNCTION_DECL && DECL_INLINE (d) && nested) || (! (TREE_CODE (d) == FUNCTION_DECL && DECL_INLINE (d) && nested)
&& ! at_eof)) && ! at_eof))
{ {
/* Defer all templates except inline functions used in another
function. */
lineno = line; lineno = line;
input_filename = file; input_filename = file;

View File

@ -969,8 +969,7 @@ compute_access (basetype_path, field)
/* Fields coming from nested anonymous unions have their DECL_CLASS_CONTEXT /* Fields coming from nested anonymous unions have their DECL_CLASS_CONTEXT
slot set to the union type rather than the record type containing slot set to the union type rather than the record type containing
the anonymous union. */ the anonymous union. */
if (context && TREE_CODE (context) == UNION_TYPE if (context && ANON_UNION_TYPE_P (context))
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (context)))
context = TYPE_CONTEXT (context); context = TYPE_CONTEXT (context);
/* Virtual function tables are never private. But we should know that /* Virtual function tables are never private. But we should know that

View File

@ -2065,8 +2065,8 @@ build_component_ref (datum, component, basetype_path, protect)
{ {
tree context = DECL_FIELD_CONTEXT (field); tree context = DECL_FIELD_CONTEXT (field);
tree base = context; tree base = context;
while (base != basetype && TYPE_NAME (base) while (!comptypes (base, basetype,1) && TYPE_NAME (base)
&& ANON_AGGRNAME_P (TYPE_IDENTIFIER (base))) && ANON_UNION_TYPE_P (base))
{ {
base = TYPE_CONTEXT (base); base = TYPE_CONTEXT (base);
} }
@ -2096,7 +2096,7 @@ build_component_ref (datum, component, basetype_path, protect)
basetype = base; basetype = base;
/* Handle things from anon unions here... */ /* Handle things from anon unions here... */
if (TYPE_NAME (context) && ANON_AGGRNAME_P (TYPE_IDENTIFIER (context))) if (TYPE_NAME (context) && ANON_UNION_TYPE_P (context))
{ {
tree subfield = lookup_anon_field (basetype, context); tree subfield = lookup_anon_field (basetype, context);
tree subdatum = build_component_ref (datum, subfield, tree subdatum = build_component_ref (datum, subfield,

View File

@ -0,0 +1,13 @@
// Build don't link:
typedef const struct {
int x;
} Test;
void foo(Test);
void foo(Test t)
{
t.x = 0; // ERROR - assignment of read-only member
return;
}

View File

@ -0,0 +1,12 @@
// Build don't link:
template <class T, int i>
struct K {
void f();
};
template <class T>
void
K<T, i>::f()
{ // ERROR - template parameters
}

View File

@ -0,0 +1,25 @@
// Build don't link:
void
print(const int& i)
{
}
template<class A>
class bar
{
public:
template<void (*B)(const A& a)>
void doit(const A& a)
{
B(a);
}
};
int
main()
{
bar<int> b;
b.template doit<print>(2);
}

View File

@ -0,0 +1,30 @@
// Build don't link:
template<class A, class B>
void foo(const A& a, const B& b)
{
}
template<class A, class B>
void foo(const A& a, const int& b)
{
}
template<class A*, class B>
void foo(const A*& a, const B& b)
{
}
template<>
void foo(const int&, const double&)
{
}
int
main()
{
foo("98239", 23);
foo(232, 1.022);
}

View File

@ -0,0 +1,21 @@
// Build don't link:
template <class A>
class TEST
{
public:
TEST (A) {}
};
template <class A>
class TEST2
{
static A i;
};
template <class A>
A TEST2 <A>::i (0);
TEST2 <TEST <int> > a;
template class TEST2 <TEST <int> >;