зеркало из https://github.com/microsoft/clang-1.git
Fix PCH emitting/reading for template arguments that contain expressions.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106996 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
dc767e3684
Коммит
17cfdeda47
|
@ -563,12 +563,30 @@ public:
|
|||
/// \brief Read preprocessed entities into the
|
||||
virtual void ReadPreprocessedEntities();
|
||||
|
||||
/// \brief Abstract interface for reading expressions.
|
||||
class ExprReader {
|
||||
public:
|
||||
virtual Expr *Read();
|
||||
};
|
||||
|
||||
/// \brief Reads a TemplateArgumentLocInfo appropriate for the
|
||||
/// given TemplateArgument kind.
|
||||
TemplateArgumentLocInfo
|
||||
GetTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind,
|
||||
const RecordData &Record, unsigned &Idx,
|
||||
ExprReader &ExprRdr);
|
||||
|
||||
/// \brief Reads a TemplateArgumentLocInfo appropriate for the
|
||||
/// given TemplateArgument kind.
|
||||
TemplateArgumentLocInfo
|
||||
GetTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind,
|
||||
const RecordData &Record, unsigned &Idx);
|
||||
|
||||
/// \brief Reads a TemplateArgumentLoc.
|
||||
TemplateArgumentLoc ReadTemplateArgumentLoc(const RecordData &Record,
|
||||
unsigned &Idx,
|
||||
ExprReader &ExprRdr);
|
||||
|
||||
/// \brief Reads a TemplateArgumentLoc.
|
||||
TemplateArgumentLoc ReadTemplateArgumentLoc(const RecordData &Record,
|
||||
unsigned &Idx);
|
||||
|
@ -700,6 +718,10 @@ public:
|
|||
/// \brief Read a template name.
|
||||
TemplateName ReadTemplateName(const RecordData &Record, unsigned &Idx);
|
||||
|
||||
/// \brief Read a template argument.
|
||||
TemplateArgument ReadTemplateArgument(const RecordData &Record,unsigned &Idx,
|
||||
ExprReader &ExprRdr);
|
||||
|
||||
/// \brief Read a template argument.
|
||||
TemplateArgument ReadTemplateArgument(const RecordData &Record,unsigned &Idx);
|
||||
|
||||
|
|
|
@ -211,6 +211,9 @@ private:
|
|||
/// file.
|
||||
unsigned NumVisibleDeclContexts;
|
||||
|
||||
/// \brief True when we are in Stmts emitting mode.
|
||||
bool EmittingStmts;
|
||||
|
||||
void WriteBlockInfoBlock();
|
||||
void WriteMetadata(ASTContext &Context, const char *isysroot);
|
||||
void WriteLanguageOptions(const LangOptions &LangOpts);
|
||||
|
@ -355,7 +358,12 @@ public:
|
|||
/// type or declaration has been written, call FlushStmts() to write
|
||||
/// the corresponding statements just after the type or
|
||||
/// declaration.
|
||||
void AddStmt(Stmt *S) { StmtsToEmit.push_back(S); }
|
||||
void AddStmt(Stmt *S) {
|
||||
if (EmittingStmts)
|
||||
WriteSubStmt(S);
|
||||
else
|
||||
StmtsToEmit.push_back(S);
|
||||
}
|
||||
|
||||
/// \brief Write the given subexpression to the bitstream.
|
||||
void WriteSubStmt(Stmt *S);
|
||||
|
|
|
@ -321,6 +321,18 @@ void PCHValidator::ReadCounter(unsigned Value) {
|
|||
// PCH reader implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Give ExprReader's VTable a home.
|
||||
Expr *PCHReader::ExprReader::Read() { return 0; }
|
||||
|
||||
namespace {
|
||||
class DeclExprReader : public PCHReader::ExprReader {
|
||||
PCHReader &Reader;
|
||||
public:
|
||||
DeclExprReader(PCHReader &reader) : Reader(reader) { }
|
||||
virtual Expr *Read() { return Reader.ReadDeclExpr(); }
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
PCHReader::PCHReader(Preprocessor &PP, ASTContext *Context,
|
||||
const char *isysroot)
|
||||
: Listener(new PCHValidator(PP, *this)), SourceMgr(PP.getSourceManager()),
|
||||
|
@ -2499,19 +2511,16 @@ QualType PCHReader::GetType(pch::TypeID ID) {
|
|||
TemplateArgumentLocInfo
|
||||
PCHReader::GetTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind,
|
||||
const RecordData &Record,
|
||||
unsigned &Index) {
|
||||
unsigned &Index, ExprReader &ExprRdr) {
|
||||
switch (Kind) {
|
||||
case TemplateArgument::Expression:
|
||||
return ReadDeclExpr();
|
||||
return ExprRdr.Read();
|
||||
case TemplateArgument::Type:
|
||||
return GetTypeSourceInfo(Record, Index);
|
||||
case TemplateArgument::Template: {
|
||||
SourceLocation
|
||||
QualStart = SourceLocation::getFromRawEncoding(Record[Index++]),
|
||||
QualEnd = SourceLocation::getFromRawEncoding(Record[Index++]),
|
||||
TemplateNameLoc = SourceLocation::getFromRawEncoding(Record[Index++]);
|
||||
return TemplateArgumentLocInfo(SourceRange(QualStart, QualEnd),
|
||||
TemplateNameLoc);
|
||||
SourceRange QualifierRange = ReadSourceRange(Record, Index);
|
||||
SourceLocation TemplateNameLoc = ReadSourceLocation(Record, Index);
|
||||
return TemplateArgumentLocInfo(QualifierRange, TemplateNameLoc);
|
||||
}
|
||||
case TemplateArgument::Null:
|
||||
case TemplateArgument::Integral:
|
||||
|
@ -2523,11 +2532,32 @@ PCHReader::GetTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind,
|
|||
return TemplateArgumentLocInfo();
|
||||
}
|
||||
|
||||
TemplateArgumentLocInfo
|
||||
PCHReader::GetTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind,
|
||||
const RecordData &Record,
|
||||
unsigned &Index) {
|
||||
DeclExprReader DeclExprReader(*this);
|
||||
return GetTemplateArgumentLocInfo(Kind, Record, Index, DeclExprReader);
|
||||
}
|
||||
|
||||
TemplateArgumentLoc
|
||||
PCHReader::ReadTemplateArgumentLoc(const RecordData &Record, unsigned &Index,
|
||||
ExprReader &ExprRdr) {
|
||||
TemplateArgument Arg = ReadTemplateArgument(Record, Index, ExprRdr);
|
||||
|
||||
if (Arg.getKind() == TemplateArgument::Expression) {
|
||||
if (Record[Index++]) // bool InfoHasSameExpr.
|
||||
return TemplateArgumentLoc(Arg, TemplateArgumentLocInfo(Arg.getAsExpr()));
|
||||
}
|
||||
return TemplateArgumentLoc(Arg, GetTemplateArgumentLocInfo(Arg.getKind(),
|
||||
Record, Index,
|
||||
ExprRdr));
|
||||
}
|
||||
|
||||
TemplateArgumentLoc
|
||||
PCHReader::ReadTemplateArgumentLoc(const RecordData &Record, unsigned &Index) {
|
||||
TemplateArgument Arg = ReadTemplateArgument(Record, Index);
|
||||
return TemplateArgumentLoc(Arg, GetTemplateArgumentLocInfo(Arg.getKind(),
|
||||
Record, Index));
|
||||
DeclExprReader DeclExprReader(*this);
|
||||
return ReadTemplateArgumentLoc(Record, Index, DeclExprReader);
|
||||
}
|
||||
|
||||
Decl *PCHReader::GetExternalDecl(uint32_t ID) {
|
||||
|
@ -2995,7 +3025,7 @@ PCHReader::ReadTemplateName(const RecordData &Record, unsigned &Idx) {
|
|||
return Context->getDependentTemplateName(NNS,
|
||||
GetIdentifierInfo(Record, Idx));
|
||||
return Context->getDependentTemplateName(NNS,
|
||||
(OverloadedOperatorKind)Record[Idx++]);
|
||||
(OverloadedOperatorKind)Record[Idx++]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3004,7 +3034,8 @@ PCHReader::ReadTemplateName(const RecordData &Record, unsigned &Idx) {
|
|||
}
|
||||
|
||||
TemplateArgument
|
||||
PCHReader::ReadTemplateArgument(const RecordData &Record, unsigned &Idx) {
|
||||
PCHReader::ReadTemplateArgument(const RecordData &Record, unsigned &Idx,
|
||||
ExprReader &ExprRdr) {
|
||||
switch ((TemplateArgument::ArgKind)Record[Idx++]) {
|
||||
case TemplateArgument::Null:
|
||||
return TemplateArgument();
|
||||
|
@ -3020,13 +3051,13 @@ PCHReader::ReadTemplateArgument(const RecordData &Record, unsigned &Idx) {
|
|||
case TemplateArgument::Template:
|
||||
return TemplateArgument(ReadTemplateName(Record, Idx));
|
||||
case TemplateArgument::Expression:
|
||||
return TemplateArgument(ReadDeclExpr());
|
||||
return TemplateArgument(ExprRdr.Read());
|
||||
case TemplateArgument::Pack: {
|
||||
unsigned NumArgs = Record[Idx++];
|
||||
llvm::SmallVector<TemplateArgument, 8> Args;
|
||||
Args.reserve(NumArgs);
|
||||
while (NumArgs--)
|
||||
Args.push_back(ReadTemplateArgument(Record, Idx));
|
||||
Args.push_back(ReadTemplateArgument(Record, Idx, ExprRdr));
|
||||
TemplateArgument TemplArg;
|
||||
TemplArg.setArgumentPack(Args.data(), Args.size(), /*CopyArgs=*/true);
|
||||
return TemplArg;
|
||||
|
@ -3037,6 +3068,12 @@ PCHReader::ReadTemplateArgument(const RecordData &Record, unsigned &Idx) {
|
|||
return TemplateArgument();
|
||||
}
|
||||
|
||||
TemplateArgument
|
||||
PCHReader::ReadTemplateArgument(const RecordData &Record, unsigned &Idx) {
|
||||
DeclExprReader DeclExprReader(*this);
|
||||
return ReadTemplateArgument(Record, Idx, DeclExprReader);
|
||||
}
|
||||
|
||||
TemplateParameterList *
|
||||
PCHReader::ReadTemplateParameterList(const RecordData &Record, unsigned &Idx) {
|
||||
SourceLocation TemplateLoc = ReadSourceLocation(Record, Idx);
|
||||
|
|
|
@ -18,6 +18,15 @@
|
|||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
||||
class StmtStackExprReader : public PCHReader::ExprReader {
|
||||
llvm::SmallVectorImpl<Stmt *>::iterator StmtI;
|
||||
public:
|
||||
StmtStackExprReader(const llvm::SmallVectorImpl<Stmt *>::iterator &stmtI)
|
||||
: StmtI(stmtI) { }
|
||||
virtual Expr *Read() { return cast_or_null<Expr>(*StmtI++); }
|
||||
};
|
||||
|
||||
class PCHStmtReader : public StmtVisitor<PCHStmtReader, unsigned> {
|
||||
PCHReader &Reader;
|
||||
const PCHReader::RecordData &Record;
|
||||
|
@ -1124,6 +1133,7 @@ unsigned PCHStmtReader::VisitCXXExprWithTemporaries(CXXExprWithTemporaries *E) {
|
|||
unsigned
|
||||
PCHStmtReader::VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E){
|
||||
VisitExpr(E);
|
||||
unsigned NumExprs = 0;
|
||||
|
||||
unsigned NumTemplateArgs = Record[Idx++];
|
||||
assert((NumTemplateArgs != 0) == E->hasExplicitTemplateArgs() &&
|
||||
|
@ -1132,8 +1142,17 @@ PCHStmtReader::VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E){
|
|||
TemplateArgumentListInfo ArgInfo;
|
||||
ArgInfo.setLAngleLoc(Reader.ReadSourceLocation(Record, Idx));
|
||||
ArgInfo.setRAngleLoc(Reader.ReadSourceLocation(Record, Idx));
|
||||
|
||||
NumExprs = Record[Idx++];
|
||||
llvm::SmallVectorImpl<Stmt *>::iterator
|
||||
StartOfExprs = StmtStack.end() - NumExprs;
|
||||
if (isa<UnresolvedMemberExpr>(E))
|
||||
--StartOfExprs; // UnresolvedMemberExpr contains an Expr;
|
||||
StmtStackExprReader ExprReader(StartOfExprs);
|
||||
for (unsigned i = 0; i != NumTemplateArgs; ++i)
|
||||
ArgInfo.addArgument(Reader.ReadTemplateArgumentLoc(Record, Idx));
|
||||
ArgInfo.addArgument(Reader.ReadTemplateArgumentLoc(Record, Idx,
|
||||
ExprReader));
|
||||
|
||||
E->initializeTemplateArgumentsFrom(ArgInfo);
|
||||
}
|
||||
|
||||
|
@ -1148,7 +1167,7 @@ PCHStmtReader::VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E){
|
|||
E->setMember(Reader.ReadDeclarationName(Record, Idx));
|
||||
E->setMemberLoc(Reader.ReadSourceLocation(Record, Idx));
|
||||
|
||||
return 1;
|
||||
return NumExprs + 1;
|
||||
}
|
||||
|
||||
unsigned
|
||||
|
@ -1167,6 +1186,7 @@ PCHStmtReader::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *E) {
|
|||
|
||||
unsigned PCHStmtReader::VisitOverloadExpr(OverloadExpr *E) {
|
||||
VisitExpr(E);
|
||||
unsigned NumExprs = 0;
|
||||
|
||||
unsigned NumTemplateArgs = Record[Idx++];
|
||||
assert((NumTemplateArgs != 0) == E->hasExplicitTemplateArgs() &&
|
||||
|
@ -1175,8 +1195,17 @@ unsigned PCHStmtReader::VisitOverloadExpr(OverloadExpr *E) {
|
|||
TemplateArgumentListInfo ArgInfo;
|
||||
ArgInfo.setLAngleLoc(Reader.ReadSourceLocation(Record, Idx));
|
||||
ArgInfo.setRAngleLoc(Reader.ReadSourceLocation(Record, Idx));
|
||||
|
||||
NumExprs = Record[Idx++];
|
||||
llvm::SmallVectorImpl<Stmt *>::iterator
|
||||
StartOfExprs = StmtStack.end() - NumExprs;
|
||||
if (isa<UnresolvedMemberExpr>(E))
|
||||
--StartOfExprs; // UnresolvedMemberExpr contains an Expr;
|
||||
StmtStackExprReader ExprReader(StartOfExprs);
|
||||
for (unsigned i = 0; i != NumTemplateArgs; ++i)
|
||||
ArgInfo.addArgument(Reader.ReadTemplateArgumentLoc(Record, Idx));
|
||||
ArgInfo.addArgument(Reader.ReadTemplateArgumentLoc(Record, Idx,
|
||||
ExprReader));
|
||||
|
||||
E->getExplicitTemplateArgs().initializeFrom(ArgInfo);
|
||||
}
|
||||
|
||||
|
@ -1193,25 +1222,25 @@ unsigned PCHStmtReader::VisitOverloadExpr(OverloadExpr *E) {
|
|||
E->setQualifier(Reader.ReadNestedNameSpecifier(Record, Idx));
|
||||
E->setQualifierRange(Reader.ReadSourceRange(Record, Idx));
|
||||
E->setNameLoc(Reader.ReadSourceLocation(Record, Idx));
|
||||
return 0;
|
||||
return NumExprs;
|
||||
}
|
||||
|
||||
unsigned PCHStmtReader::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *E) {
|
||||
VisitOverloadExpr(E);
|
||||
unsigned NumExprs = VisitOverloadExpr(E);
|
||||
E->setArrow(Record[Idx++]);
|
||||
E->setHasUnresolvedUsing(Record[Idx++]);
|
||||
E->setBase(cast_or_null<Expr>(StmtStack.back()));
|
||||
E->setBaseType(Reader.GetType(Record[Idx++]));
|
||||
E->setOperatorLoc(Reader.ReadSourceLocation(Record, Idx));
|
||||
return 1;
|
||||
return NumExprs + 1;
|
||||
}
|
||||
|
||||
unsigned PCHStmtReader::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
|
||||
VisitOverloadExpr(E);
|
||||
unsigned NumExprs = VisitOverloadExpr(E);
|
||||
E->setRequiresADL(Record[Idx++]);
|
||||
E->setOverloaded(Record[Idx++]);
|
||||
E->setNamingClass(cast_or_null<CXXRecordDecl>(Reader.GetDecl(Record[Idx++])));
|
||||
return 0;
|
||||
return NumExprs;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2057,7 +2057,7 @@ void PCHWriter::SetSelectorOffset(Selector Sel, uint32_t Offset) {
|
|||
PCHWriter::PCHWriter(llvm::BitstreamWriter &Stream)
|
||||
: Stream(Stream), NextTypeID(pch::NUM_PREDEF_TYPE_IDS),
|
||||
NumStatements(0), NumMacros(0), NumLexicalDeclContexts(0),
|
||||
NumVisibleDeclContexts(0) { }
|
||||
NumVisibleDeclContexts(0), EmittingStmts(false) { }
|
||||
|
||||
void PCHWriter::WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls,
|
||||
const char *isysroot) {
|
||||
|
@ -2302,10 +2302,8 @@ void PCHWriter::AddTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind,
|
|||
AddTypeSourceInfo(Arg.getAsTypeSourceInfo(), Record);
|
||||
break;
|
||||
case TemplateArgument::Template:
|
||||
Record.push_back(
|
||||
Arg.getTemplateQualifierRange().getBegin().getRawEncoding());
|
||||
Record.push_back(Arg.getTemplateQualifierRange().getEnd().getRawEncoding());
|
||||
Record.push_back(Arg.getTemplateNameLoc().getRawEncoding());
|
||||
AddSourceRange(Arg.getTemplateQualifierRange(), Record);
|
||||
AddSourceLocation(Arg.getTemplateNameLoc(), Record);
|
||||
break;
|
||||
case TemplateArgument::Null:
|
||||
case TemplateArgument::Integral:
|
||||
|
@ -2318,6 +2316,14 @@ void PCHWriter::AddTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind,
|
|||
void PCHWriter::AddTemplateArgumentLoc(const TemplateArgumentLoc &Arg,
|
||||
RecordData &Record) {
|
||||
AddTemplateArgument(Arg.getArgument(), Record);
|
||||
|
||||
if (Arg.getArgument().getKind() == TemplateArgument::Expression) {
|
||||
bool InfoHasSameExpr
|
||||
= Arg.getArgument().getAsExpr() == Arg.getLocInfo().getAsExpr();
|
||||
Record.push_back(InfoHasSameExpr);
|
||||
if (InfoHasSameExpr)
|
||||
return; // Avoid storing the same expr twice.
|
||||
}
|
||||
AddTemplateArgumentLocInfo(Arg.getArgument().getKind(), Arg.getLocInfo(),
|
||||
Record);
|
||||
}
|
||||
|
|
|
@ -1025,6 +1025,40 @@ void PCHStmtWriter::VisitCXXExprWithTemporaries(CXXExprWithTemporaries *E) {
|
|||
Code = pch::EXPR_CXX_EXPR_WITH_TEMPORARIES;
|
||||
}
|
||||
|
||||
/// \brief Return the number of Exprs contained in the given TemplateArgument.
|
||||
static unsigned NumExprsContainedIn(const TemplateArgument Arg) {
|
||||
switch (Arg.getKind()) {
|
||||
default: break;
|
||||
case TemplateArgument::Expression:
|
||||
return 1;
|
||||
case TemplateArgument::Pack: {
|
||||
unsigned Count = 0;
|
||||
for (TemplateArgument::pack_iterator I=Arg.pack_begin(), E=Arg.pack_end();
|
||||
I != E; ++I)
|
||||
Count += NumExprsContainedIn(*I);
|
||||
return Count;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Return the number of Exprs contained in the given
|
||||
/// TemplateArgumentLoc.
|
||||
static
|
||||
unsigned NumExprsContainedIn(const TemplateArgumentLoc *Args,unsigned NumArgs) {
|
||||
unsigned Count = 0;
|
||||
for (unsigned i=0; i != NumArgs; ++i) {
|
||||
const TemplateArgument &TemplA = Args[i].getArgument();
|
||||
Count += NumExprsContainedIn(TemplA);
|
||||
if (TemplA.getKind() == TemplateArgument::Expression &&
|
||||
TemplA.getAsExpr() != Args[i].getLocInfo().getAsExpr())
|
||||
++Count; // 1 in TemplateArgumentLocInfo.
|
||||
}
|
||||
|
||||
return Count;
|
||||
}
|
||||
|
||||
void
|
||||
PCHStmtWriter::VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E){
|
||||
VisitExpr(E);
|
||||
|
@ -1035,6 +1069,8 @@ PCHStmtWriter::VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E){
|
|||
Record.push_back(E->getNumTemplateArgs());
|
||||
Writer.AddSourceLocation(E->getLAngleLoc(), Record);
|
||||
Writer.AddSourceLocation(E->getRAngleLoc(), Record);
|
||||
Record.push_back(NumExprsContainedIn(E->getTemplateArgs(),
|
||||
E->getNumTemplateArgs()));
|
||||
for (int i=0, e = E->getNumTemplateArgs(); i != e; ++i)
|
||||
Writer.AddTemplateArgumentLoc(E->getTemplateArgs()[i], Record);
|
||||
} else {
|
||||
|
@ -1080,6 +1116,8 @@ void PCHStmtWriter::VisitOverloadExpr(OverloadExpr *E) {
|
|||
Record.push_back(Args.NumTemplateArgs);
|
||||
Writer.AddSourceLocation(Args.LAngleLoc, Record);
|
||||
Writer.AddSourceLocation(Args.RAngleLoc, Record);
|
||||
Record.push_back(NumExprsContainedIn(Args.getTemplateArgs(),
|
||||
Args.NumTemplateArgs));
|
||||
for (unsigned i=0; i != Args.NumTemplateArgs; ++i)
|
||||
Writer.AddTemplateArgumentLoc(Args.getTemplateArgs()[i], Record);
|
||||
} else {
|
||||
|
@ -1178,6 +1216,8 @@ void PCHWriter::WriteSubStmt(Stmt *S) {
|
|||
void PCHWriter::FlushStmts() {
|
||||
RecordData Record;
|
||||
PCHStmtWriter Writer(*this, Record);
|
||||
|
||||
EmittingStmts = true;
|
||||
|
||||
for (unsigned I = 0, N = StmtsToEmit.size(); I != N; ++I) {
|
||||
++NumStatements;
|
||||
|
@ -1209,5 +1249,7 @@ void PCHWriter::FlushStmts() {
|
|||
Stream.EmitRecord(pch::STMT_STOP, Record);
|
||||
}
|
||||
|
||||
EmittingStmts = false;
|
||||
|
||||
StmtsToEmit.clear();
|
||||
}
|
||||
|
|
|
@ -15,9 +15,13 @@ struct S<int, float> {
|
|||
static void explicit_special();
|
||||
};
|
||||
|
||||
template <int x>
|
||||
int tmpl_f2() { return x; }
|
||||
|
||||
template <typename T, int y>
|
||||
T templ_f(T x) {
|
||||
int z = templ_f<int, 5>(3);
|
||||
z = tmpl_f2<y+2>();
|
||||
return x+y;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче