mirror of
https://github.com/intel/llvm.git
synced 2026-02-02 02:00:03 +08:00
Implement support for template template parameter packs, e.g.,
template<template<class> class ...Metafunctions>
struct apply_to_each;
llvm-svn: 122874
This commit is contained in:
@@ -1084,18 +1084,22 @@ class TemplateTemplateParmDecl
|
||||
/// Whether or not the default argument was inherited.
|
||||
bool DefaultArgumentWasInherited;
|
||||
|
||||
/// \brief Whether this parameter is a parameter pack.
|
||||
bool ParameterPack;
|
||||
|
||||
TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L,
|
||||
unsigned D, unsigned P,
|
||||
unsigned D, unsigned P, bool ParameterPack,
|
||||
IdentifierInfo *Id, TemplateParameterList *Params)
|
||||
: TemplateDecl(TemplateTemplateParm, DC, L, Id, Params),
|
||||
TemplateParmPosition(D, P), DefaultArgument(),
|
||||
DefaultArgumentWasInherited(false)
|
||||
DefaultArgumentWasInherited(false), ParameterPack(ParameterPack)
|
||||
{ }
|
||||
|
||||
public:
|
||||
static TemplateTemplateParmDecl *Create(ASTContext &C, DeclContext *DC,
|
||||
SourceLocation L, unsigned D,
|
||||
unsigned P, IdentifierInfo *Id,
|
||||
unsigned P, bool ParameterPack,
|
||||
IdentifierInfo *Id,
|
||||
TemplateParameterList *Params);
|
||||
|
||||
using TemplateParmPosition::getDepth;
|
||||
@@ -1108,7 +1112,7 @@ public:
|
||||
/// \code
|
||||
/// template<template <class T> ...MetaFunctions> struct Apply;
|
||||
/// \endcode
|
||||
bool isParameterPack() const { return /*FIXME: variadic templates*/false; }
|
||||
bool isParameterPack() const { return ParameterPack; }
|
||||
|
||||
/// \brief Determine whether this template parameter has a default
|
||||
/// argument.
|
||||
|
||||
@@ -2845,12 +2845,13 @@ public:
|
||||
Decl *ActOnTemplateTemplateParameter(Scope *S,
|
||||
SourceLocation TmpLoc,
|
||||
TemplateParamsTy *Params,
|
||||
SourceLocation EllipsisLoc,
|
||||
IdentifierInfo *ParamName,
|
||||
SourceLocation ParamNameLoc,
|
||||
unsigned Depth,
|
||||
unsigned Position,
|
||||
SourceLocation EqualLoc,
|
||||
const ParsedTemplateArgument &DefaultArg);
|
||||
ParsedTemplateArgument DefaultArg);
|
||||
|
||||
TemplateParamsTy *
|
||||
ActOnTemplateParameterList(unsigned Depth,
|
||||
|
||||
@@ -51,7 +51,7 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID,
|
||||
TemplateTemplateParmDecl *Parm) {
|
||||
ID.AddInteger(Parm->getDepth());
|
||||
ID.AddInteger(Parm->getPosition());
|
||||
// FIXME: Parameter pack
|
||||
ID.AddBoolean(Parm->isParameterPack());
|
||||
|
||||
TemplateParameterList *Params = Parm->getTemplateParameters();
|
||||
ID.AddInteger(Params->size());
|
||||
@@ -66,7 +66,7 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID,
|
||||
|
||||
if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) {
|
||||
ID.AddInteger(1);
|
||||
// FIXME: Parameter pack
|
||||
ID.AddBoolean(NTTP->isParameterPack());
|
||||
ID.AddPointer(NTTP->getType().getAsOpaquePtr());
|
||||
continue;
|
||||
}
|
||||
@@ -119,7 +119,9 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
|
||||
TemplateTemplateParmDecl *CanonTTP
|
||||
= TemplateTemplateParmDecl::Create(*this, getTranslationUnitDecl(),
|
||||
SourceLocation(), TTP->getDepth(),
|
||||
TTP->getPosition(), 0,
|
||||
TTP->getPosition(),
|
||||
TTP->isParameterPack(),
|
||||
0,
|
||||
TemplateParameterList::Create(*this, SourceLocation(),
|
||||
SourceLocation(),
|
||||
CanonParams.data(),
|
||||
|
||||
@@ -3444,6 +3444,7 @@ ASTNodeImporter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
|
||||
return TemplateTemplateParmDecl::Create(Importer.getToContext(),
|
||||
Importer.getToContext().getTranslationUnitDecl(),
|
||||
Loc, D->getDepth(), D->getPosition(),
|
||||
D->isParameterPack(),
|
||||
Name.getAsIdentifierInfo(),
|
||||
TemplateParams);
|
||||
}
|
||||
|
||||
@@ -111,8 +111,11 @@ bool Decl::isTemplateParameterPack() const {
|
||||
if (const TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(this))
|
||||
return TTP->isParameterPack();
|
||||
if (const NonTypeTemplateParmDecl *NTTP
|
||||
= llvm::dyn_cast<NonTypeTemplateParmDecl>(this))
|
||||
= dyn_cast<NonTypeTemplateParmDecl>(this))
|
||||
return NTTP->isParameterPack();
|
||||
if (const TemplateTemplateParmDecl *TTP
|
||||
= dyn_cast<TemplateTemplateParmDecl>(this))
|
||||
return TTP->isParameterPack();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -699,8 +699,11 @@ void DeclPrinter::VisitTemplateDecl(TemplateDecl *D) {
|
||||
|
||||
Out << "> ";
|
||||
|
||||
if (isa<TemplateTemplateParmDecl>(D)) {
|
||||
Out << "class " << D->getName();
|
||||
if (TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
|
||||
Out << "class ";
|
||||
if (TTP->isParameterPack())
|
||||
Out << "...";
|
||||
Out << D->getName();
|
||||
} else {
|
||||
Visit(D->getTemplatedDecl());
|
||||
}
|
||||
|
||||
@@ -412,9 +412,10 @@ SourceLocation NonTypeTemplateParmDecl::getDefaultArgumentLoc() const {
|
||||
TemplateTemplateParmDecl *
|
||||
TemplateTemplateParmDecl::Create(ASTContext &C, DeclContext *DC,
|
||||
SourceLocation L, unsigned D, unsigned P,
|
||||
IdentifierInfo *Id,
|
||||
bool ParameterPack, IdentifierInfo *Id,
|
||||
TemplateParameterList *Params) {
|
||||
return new (C) TemplateTemplateParmDecl(DC, L, D, P, Id, Params);
|
||||
return new (C) TemplateTemplateParmDecl(DC, L, D, P, ParameterPack, Id,
|
||||
Params);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -903,7 +903,7 @@ void StmtProfiler::VisitDecl(Decl *D) {
|
||||
if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D)) {
|
||||
ID.AddInteger(NTTP->getDepth());
|
||||
ID.AddInteger(NTTP->getIndex());
|
||||
ID.AddInteger(NTTP->isParameterPack());
|
||||
ID.AddBoolean(NTTP->isParameterPack());
|
||||
VisitType(NTTP->getType());
|
||||
return;
|
||||
}
|
||||
@@ -921,6 +921,7 @@ void StmtProfiler::VisitDecl(Decl *D) {
|
||||
if (TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
|
||||
ID.AddInteger(TTP->getDepth());
|
||||
ID.AddInteger(TTP->getIndex());
|
||||
ID.AddBoolean(TTP->isParameterPack());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,12 +421,14 @@ bool Parser::isStartOfTemplateTypeParameter() {
|
||||
/// parameter-declaration
|
||||
///
|
||||
/// type-parameter: (see below)
|
||||
/// 'class' ...[opt][C++0x] identifier[opt]
|
||||
/// 'class' ...[opt] identifier[opt]
|
||||
/// 'class' identifier[opt] '=' type-id
|
||||
/// 'typename' ...[opt][C++0x] identifier[opt]
|
||||
/// 'typename' ...[opt] identifier[opt]
|
||||
/// 'typename' identifier[opt] '=' type-id
|
||||
/// 'template' ...[opt][C++0x] '<' template-parameter-list '>' 'class' identifier[opt]
|
||||
/// 'template' '<' template-parameter-list '>' 'class' identifier[opt] = id-expression
|
||||
/// 'template' '<' template-parameter-list '>'
|
||||
/// 'class' ...[opt] identifier[opt]
|
||||
/// 'template' '<' template-parameter-list '>' 'class' identifier[opt]
|
||||
/// = id-expression
|
||||
Decl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
|
||||
if (isStartOfTemplateTypeParameter())
|
||||
return ParseTypeParameter(Depth, Position);
|
||||
@@ -502,8 +504,10 @@ Decl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
|
||||
/// template parameters.
|
||||
///
|
||||
/// type-parameter: [C++ temp.param]
|
||||
/// 'template' '<' template-parameter-list '>' 'class' identifier[opt]
|
||||
/// 'template' '<' template-parameter-list '>' 'class' identifier[opt] = id-expression
|
||||
/// 'template' '<' template-parameter-list '>' 'class'
|
||||
/// ...[opt] identifier[opt]
|
||||
/// 'template' '<' template-parameter-list '>' 'class' identifier[opt]
|
||||
/// = id-expression
|
||||
Decl *
|
||||
Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
|
||||
assert(Tok.is(tok::kw_template) && "Expected 'template' keyword");
|
||||
@@ -529,6 +533,15 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
|
||||
}
|
||||
SourceLocation ClassLoc = ConsumeToken();
|
||||
|
||||
// Parse the ellipsis, if given.
|
||||
SourceLocation EllipsisLoc;
|
||||
if (Tok.is(tok::ellipsis)) {
|
||||
EllipsisLoc = ConsumeToken();
|
||||
|
||||
if (!getLang().CPlusPlus0x)
|
||||
Diag(EllipsisLoc, diag::err_variadic_templates);
|
||||
}
|
||||
|
||||
// Get the identifier, if given.
|
||||
SourceLocation NameLoc;
|
||||
IdentifierInfo* ParamName = 0;
|
||||
@@ -569,9 +582,9 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
|
||||
}
|
||||
|
||||
return Actions.ActOnTemplateTemplateParameter(getCurScope(), TemplateLoc,
|
||||
ParamList, ParamName,
|
||||
NameLoc, Depth, Position,
|
||||
EqualLoc, DefaultArg);
|
||||
ParamList, EllipsisLoc,
|
||||
ParamName, NameLoc, Depth,
|
||||
Position, EqualLoc, DefaultArg);
|
||||
}
|
||||
|
||||
/// ParseNonTypeTemplateParameter - Handle the parsing of non-type
|
||||
|
||||
@@ -522,6 +522,14 @@ Decl *Sema::ActOnTypeParameter(Scope *S, bool Typename, bool Ellipsis,
|
||||
IdResolver.AddDecl(Param);
|
||||
}
|
||||
|
||||
// C++0x [temp.param]p9:
|
||||
// A default template-argument may be specified for any kind of
|
||||
// template-parameter that is not a template parameter pack.
|
||||
if (DefaultArg && Ellipsis) {
|
||||
Diag(EqualLoc, diag::err_template_param_pack_default_arg);
|
||||
DefaultArg = ParsedType();
|
||||
}
|
||||
|
||||
// Handle the default argument, if provided.
|
||||
if (DefaultArg) {
|
||||
TypeSourceInfo *DefaultTInfo;
|
||||
@@ -529,14 +537,6 @@ Decl *Sema::ActOnTypeParameter(Scope *S, bool Typename, bool Ellipsis,
|
||||
|
||||
assert(DefaultTInfo && "expected source information for type");
|
||||
|
||||
// C++0x [temp.param]p9:
|
||||
// A default template-argument may be specified for any kind of
|
||||
// template-parameter that is not a template parameter pack.
|
||||
if (Ellipsis) {
|
||||
Diag(EqualLoc, diag::err_template_param_pack_default_arg);
|
||||
return Param;
|
||||
}
|
||||
|
||||
// Check for unexpanded parameter packs.
|
||||
if (DiagnoseUnexpandedParameterPack(Loc, DefaultTInfo,
|
||||
UPPC_DefaultArgument))
|
||||
@@ -647,16 +647,16 @@ Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
|
||||
IdResolver.AddDecl(Param);
|
||||
}
|
||||
|
||||
// C++0x [temp.param]p9:
|
||||
// A default template-argument may be specified for any kind of
|
||||
// template-parameter that is not a template parameter pack.
|
||||
if (Default && IsParameterPack) {
|
||||
Diag(EqualLoc, diag::err_template_param_pack_default_arg);
|
||||
Default = 0;
|
||||
}
|
||||
|
||||
// Check the well-formedness of the default template argument, if provided.
|
||||
if (Default) {
|
||||
// C++0x [temp.param]p9:
|
||||
// A default template-argument may be specified for any kind of
|
||||
// template-parameter that is not a template parameter pack.
|
||||
if (IsParameterPack) {
|
||||
Diag(EqualLoc, diag::err_template_param_pack_default_arg);
|
||||
return Param;
|
||||
}
|
||||
|
||||
// Check for unexpanded parameter packs.
|
||||
if (DiagnoseUnexpandedParameterPack(Default, UPPC_DefaultArgument))
|
||||
return Param;
|
||||
@@ -679,21 +679,24 @@ Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
|
||||
Decl *Sema::ActOnTemplateTemplateParameter(Scope* S,
|
||||
SourceLocation TmpLoc,
|
||||
TemplateParamsTy *Params,
|
||||
SourceLocation EllipsisLoc,
|
||||
IdentifierInfo *Name,
|
||||
SourceLocation NameLoc,
|
||||
unsigned Depth,
|
||||
unsigned Position,
|
||||
SourceLocation EqualLoc,
|
||||
const ParsedTemplateArgument &Default) {
|
||||
ParsedTemplateArgument Default) {
|
||||
assert(S->isTemplateParamScope() &&
|
||||
"Template template parameter not in template parameter scope!");
|
||||
|
||||
// Construct the parameter object.
|
||||
bool IsParameterPack = EllipsisLoc.isValid();
|
||||
// FIXME: Pack-ness is dropped
|
||||
TemplateTemplateParmDecl *Param =
|
||||
TemplateTemplateParmDecl::Create(Context, Context.getTranslationUnitDecl(),
|
||||
NameLoc.isInvalid()? TmpLoc : NameLoc,
|
||||
Depth, Position, Name,
|
||||
Params);
|
||||
Depth, Position, IsParameterPack,
|
||||
Name, Params);
|
||||
|
||||
// If the template template parameter has a name, then link the identifier
|
||||
// into the scope and lookup mechanisms.
|
||||
@@ -708,6 +711,14 @@ Decl *Sema::ActOnTemplateTemplateParameter(Scope* S,
|
||||
Param->setInvalidDecl();
|
||||
}
|
||||
|
||||
// C++0x [temp.param]p9:
|
||||
// A default template-argument may be specified for any kind of
|
||||
// template-parameter that is not a template parameter pack.
|
||||
if (IsParameterPack && !Default.isInvalid()) {
|
||||
Diag(EqualLoc, diag::err_template_param_pack_default_arg);
|
||||
Default = ParsedTemplateArgument();
|
||||
}
|
||||
|
||||
if (!Default.isInvalid()) {
|
||||
// Check only that we have a template template argument. We don't want to
|
||||
// try to check well-formedness now, because our template template parameter
|
||||
@@ -1212,7 +1223,7 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
|
||||
// new declaration.
|
||||
SawDefaultArgument = true;
|
||||
// FIXME: We need to create a new kind of "default argument"
|
||||
// expression that points to a previous template template
|
||||
// expression that points to a previous non-type template
|
||||
// parameter.
|
||||
NewNonTypeParm->setDefaultArgument(
|
||||
OldNonTypeParm->getDefaultArgument(),
|
||||
|
||||
@@ -697,9 +697,22 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
|
||||
TTP->getPosition()))
|
||||
return D;
|
||||
|
||||
// FIXME: Variadic templates index substitution.
|
||||
TemplateName Template
|
||||
= TemplateArgs(TTP->getDepth(), TTP->getPosition()).getAsTemplate();
|
||||
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
|
||||
|
||||
if (TTP->isParameterPack()) {
|
||||
assert(Arg.getKind() == TemplateArgument::Pack &&
|
||||
"Missing argument pack");
|
||||
|
||||
if (getSema().ArgumentPackSubstitutionIndex == -1) {
|
||||
// FIXME: Variadic templates fun case.
|
||||
getSema().Diag(Loc, diag::err_pack_expansion_mismatch_unsupported);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Arg = Arg.pack_begin()[getSema().ArgumentPackSubstitutionIndex];
|
||||
}
|
||||
|
||||
TemplateName Template = Arg.getAsTemplate();
|
||||
assert(!Template.isNull() && Template.getAsTemplateDecl() &&
|
||||
"Wrong kind of template template argument");
|
||||
return Template.getAsTemplateDecl();
|
||||
|
||||
@@ -1542,8 +1542,8 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl(
|
||||
TemplateTemplateParmDecl *Param
|
||||
= TemplateTemplateParmDecl::Create(SemaRef.Context, Owner, D->getLocation(),
|
||||
D->getDepth() - TemplateArgs.getNumLevels(),
|
||||
D->getPosition(), D->getIdentifier(),
|
||||
InstParams);
|
||||
D->getPosition(), D->isParameterPack(),
|
||||
D->getIdentifier(), InstParams);
|
||||
Param->setDefaultArgument(D->getDefaultArgument(), false);
|
||||
|
||||
// Introduce this template parameter's instantiation into the instantiation
|
||||
|
||||
@@ -78,7 +78,16 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: Record occurrences of template template parameter packs.
|
||||
/// \brief Record occurrences of template template parameter packs.
|
||||
bool TraverseTemplateName(TemplateName Template) {
|
||||
if (TemplateTemplateParmDecl *TTP
|
||||
= dyn_cast_or_null<TemplateTemplateParmDecl>(
|
||||
Template.getAsTemplateDecl()))
|
||||
if (TTP->isParameterPack())
|
||||
Unexpanded.push_back(std::make_pair(TTP, SourceLocation()));
|
||||
|
||||
return inherited::TraverseTemplateName(Template);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Pruning the search for unexpanded parameter packs.
|
||||
@@ -556,7 +565,6 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S,
|
||||
SourceLocation RParenLoc) {
|
||||
// C++0x [expr.sizeof]p5:
|
||||
// The identifier in a sizeof... expression shall name a parameter pack.
|
||||
|
||||
LookupResult R(*this, &Name, NameLoc, LookupOrdinaryName);
|
||||
LookupName(R, S);
|
||||
|
||||
|
||||
@@ -1173,6 +1173,7 @@ void ASTDeclReader::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
|
||||
TemplateArgumentLoc Arg = Reader.ReadTemplateArgumentLoc(F, Record, Idx);
|
||||
bool IsInherited = Record[Idx++];
|
||||
D->setDefaultArgument(Arg, IsInherited);
|
||||
D->ParameterPack = Record[Idx++];
|
||||
}
|
||||
|
||||
void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) {
|
||||
@@ -1433,7 +1434,8 @@ Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) {
|
||||
QualType(), false, 0);
|
||||
break;
|
||||
case DECL_TEMPLATE_TEMPLATE_PARM:
|
||||
D = TemplateTemplateParmDecl::Create(*Context, 0, SourceLocation(),0,0,0,0);
|
||||
D = TemplateTemplateParmDecl::Create(*Context, 0, SourceLocation(), 0, 0,
|
||||
false, 0, 0);
|
||||
break;
|
||||
case DECL_STATIC_ASSERT:
|
||||
D = StaticAssertDecl::Create(*Context, 0, SourceLocation(), 0, 0);
|
||||
|
||||
@@ -1011,6 +1011,7 @@ void ASTDeclWriter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
|
||||
// Rest of TemplateTemplateParmDecl.
|
||||
Writer.AddTemplateArgumentLoc(D->getDefaultArgument(), Record);
|
||||
Record.push_back(D->defaultArgumentWasInherited());
|
||||
Record.push_back(D->isParameterPack());
|
||||
Code = serialization::DECL_TEMPLATE_TEMPLATE_PARM;
|
||||
}
|
||||
|
||||
|
||||
@@ -138,3 +138,29 @@ namespace Indices {
|
||||
int check0[is_same<build_indices<5>::type,
|
||||
int_tuple<0, 1, 2, 3, 4>>::value? 1 : -1];
|
||||
}
|
||||
|
||||
namespace TemplateTemplateApply {
|
||||
template<typename T, template<class> class ...Meta>
|
||||
struct apply_each {
|
||||
typedef tuple<typename Meta<T>::type...> type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct add_reference {
|
||||
typedef T& type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct add_pointer {
|
||||
typedef T* type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct add_const {
|
||||
typedef const T type;
|
||||
};
|
||||
|
||||
int check0[is_same<apply_each<int,
|
||||
add_reference, add_pointer, add_const>::type,
|
||||
tuple<int&, int*, int const>>::value? 1 : -1];
|
||||
}
|
||||
|
||||
@@ -154,6 +154,11 @@ void TestPPNameFunc(int i) {
|
||||
f(static_cast<Types>(i)); // expected-error{{expression contains unexpanded parameter pack 'Types'}}
|
||||
}
|
||||
|
||||
template<typename T, template<class> class ...Meta>
|
||||
struct TestUnexpandedTTP {
|
||||
typedef tuple<typename Meta<T>::type> type; // expected-error{{declaration type contains unexpanded parameter pack 'Meta'}}
|
||||
};
|
||||
|
||||
// Test for unexpanded parameter packs in declarations.
|
||||
// FIXME: Attributes?
|
||||
template<typename T, typename... Types>
|
||||
|
||||
@@ -19,3 +19,5 @@ template<template<typename T> class> struct X1tt; // expected-error{{template ty
|
||||
|
||||
template<template<typename T> class> struct X2tt; // expected-note{{previous template type parameter declared here}}
|
||||
template<template<typename ...T> class> struct X2tt; // expected-error{{template type parameter pack conflicts with previous template type parameter}}
|
||||
|
||||
// FIXME: Add checks for non-type template parameter packs, template parameter packs
|
||||
|
||||
@@ -8,3 +8,7 @@ struct X0;
|
||||
template<int ...Values = 0> // expected-error{{template parameter pack cannot have a default argument}}
|
||||
struct X1;
|
||||
|
||||
template<typename T> struct vector;
|
||||
|
||||
template<template<class> class ...Templates = vector> // expected-error{{template parameter pack cannot have a default argument}}
|
||||
struct X2;
|
||||
|
||||
Reference in New Issue
Block a user