Track the difference between
-- a constructor list initialization that unpacked an initializer list into constructor arguments and -- a list initialization that created as std::initializer_list and passed it as the first argument to a constructor in the AST. Use this flag while instantiating templates to provide the right semantics for the resulting initialization. llvm-svn: 213224
This commit is contained in:
parent
68c89c2480
commit
f8adcdc436
|
@ -1077,6 +1077,7 @@ private:
|
|||
bool Elidable : 1;
|
||||
bool HadMultipleCandidates : 1;
|
||||
bool ListInitialization : 1;
|
||||
bool StdInitListInitialization : 1;
|
||||
bool ZeroInitialization : 1;
|
||||
unsigned ConstructKind : 2;
|
||||
Stmt **Args;
|
||||
|
@ -1088,6 +1089,7 @@ protected:
|
|||
ArrayRef<Expr *> Args,
|
||||
bool HadMultipleCandidates,
|
||||
bool ListInitialization,
|
||||
bool StdInitListInitialization,
|
||||
bool ZeroInitialization,
|
||||
ConstructionKind ConstructKind,
|
||||
SourceRange ParenOrBraceRange);
|
||||
|
@ -1114,6 +1116,7 @@ public:
|
|||
ArrayRef<Expr *> Args,
|
||||
bool HadMultipleCandidates,
|
||||
bool ListInitialization,
|
||||
bool StdInitListInitialization,
|
||||
bool ZeroInitialization,
|
||||
ConstructionKind ConstructKind,
|
||||
SourceRange ParenOrBraceRange);
|
||||
|
@ -1137,6 +1140,13 @@ public:
|
|||
bool isListInitialization() const { return ListInitialization; }
|
||||
void setListInitialization(bool V) { ListInitialization = V; }
|
||||
|
||||
/// \brief Whether this constructor call was written as list-initialization,
|
||||
/// but was interpreted as forming a std::initializer_list<T> from the list
|
||||
/// and passing that as a single constructor argument.
|
||||
/// See C++11 [over.match.list]p1 bullet 1.
|
||||
bool isStdInitListInitialization() const { return StdInitListInitialization; }
|
||||
void setStdInitListInitialization(bool V) { StdInitListInitialization = V; }
|
||||
|
||||
/// \brief Whether this construction first requires
|
||||
/// zero-initialization before the initializer is called.
|
||||
bool requiresZeroInitialization() const { return ZeroInitialization; }
|
||||
|
@ -1272,6 +1282,7 @@ public:
|
|||
SourceRange ParenOrBraceRange,
|
||||
bool HadMultipleCandidates,
|
||||
bool ListInitialization,
|
||||
bool StdInitListInitialization,
|
||||
bool ZeroInitialization);
|
||||
explicit CXXTemporaryObjectExpr(EmptyShell Empty)
|
||||
: CXXConstructExpr(CXXTemporaryObjectExprClass, Empty), Type() { }
|
||||
|
|
|
@ -671,8 +671,6 @@ public:
|
|||
SK_ConversionSequenceNoNarrowing,
|
||||
/// \brief Perform list-initialization without a constructor.
|
||||
SK_ListInitialization,
|
||||
/// \brief Perform list-initialization with an initializer list constructor.
|
||||
SK_ListConstructorCall,
|
||||
/// \brief Unwrap the single-element initializer list for a reference.
|
||||
SK_UnwrapInitList,
|
||||
/// \brief Rewrap the single-element initializer list for a reference.
|
||||
|
@ -705,6 +703,9 @@ public:
|
|||
SK_ProduceObjCObject,
|
||||
/// \brief Construct a std::initializer_list from an initializer list.
|
||||
SK_StdInitializerList,
|
||||
/// \brief Perform initialization via a constructor taking a single
|
||||
/// std::initializer_list argument.
|
||||
SK_StdInitializerListConstructorCall,
|
||||
/// \brief Initialize an OpenCL sampler from an integer.
|
||||
SK_OCLSamplerInit,
|
||||
/// \brief Passing zero to a function where OpenCL event_t is expected.
|
||||
|
|
|
@ -3858,16 +3858,18 @@ public:
|
|||
BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
|
||||
CXXConstructorDecl *Constructor, MultiExprArg Exprs,
|
||||
bool HadMultipleCandidates, bool IsListInitialization,
|
||||
bool IsStdInitListInitialization,
|
||||
bool RequiresZeroInit, unsigned ConstructKind,
|
||||
SourceRange ParenRange);
|
||||
|
||||
// FIXME: Can re remove this and have the above BuildCXXConstructExpr check if
|
||||
// FIXME: Can we remove this and have the above BuildCXXConstructExpr check if
|
||||
// the constructor can be elidable?
|
||||
ExprResult
|
||||
BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
|
||||
CXXConstructorDecl *Constructor, bool Elidable,
|
||||
MultiExprArg Exprs, bool HadMultipleCandidates,
|
||||
bool IsListInitialization, bool RequiresZeroInit,
|
||||
bool IsListInitialization,
|
||||
bool IsStdInitListInitialization, bool RequiresZeroInit,
|
||||
unsigned ConstructKind, SourceRange ParenRange);
|
||||
|
||||
/// BuildCXXDefaultArgExpr - Creates a CXXDefaultArgExpr, instantiating
|
||||
|
|
|
@ -806,13 +806,16 @@ CXXTemporaryObjectExpr::CXXTemporaryObjectExpr(const ASTContext &C,
|
|||
SourceRange ParenOrBraceRange,
|
||||
bool HadMultipleCandidates,
|
||||
bool ListInitialization,
|
||||
bool StdInitListInitialization,
|
||||
bool ZeroInitialization)
|
||||
: CXXConstructExpr(C, CXXTemporaryObjectExprClass,
|
||||
Type->getType().getNonReferenceType(),
|
||||
Type->getTypeLoc().getBeginLoc(),
|
||||
Cons, false, Args,
|
||||
HadMultipleCandidates,
|
||||
ListInitialization, ZeroInitialization,
|
||||
ListInitialization,
|
||||
StdInitListInitialization,
|
||||
ZeroInitialization,
|
||||
CXXConstructExpr::CK_Complete, ParenOrBraceRange),
|
||||
Type(Type) {
|
||||
}
|
||||
|
@ -834,12 +837,14 @@ CXXConstructExpr *CXXConstructExpr::Create(const ASTContext &C, QualType T,
|
|||
ArrayRef<Expr*> Args,
|
||||
bool HadMultipleCandidates,
|
||||
bool ListInitialization,
|
||||
bool StdInitListInitialization,
|
||||
bool ZeroInitialization,
|
||||
ConstructionKind ConstructKind,
|
||||
SourceRange ParenOrBraceRange) {
|
||||
return new (C) CXXConstructExpr(C, CXXConstructExprClass, T, Loc, D,
|
||||
Elidable, Args,
|
||||
HadMultipleCandidates, ListInitialization,
|
||||
StdInitListInitialization,
|
||||
ZeroInitialization, ConstructKind,
|
||||
ParenOrBraceRange);
|
||||
}
|
||||
|
@ -850,6 +855,7 @@ CXXConstructExpr::CXXConstructExpr(const ASTContext &C, StmtClass SC,
|
|||
ArrayRef<Expr*> args,
|
||||
bool HadMultipleCandidates,
|
||||
bool ListInitialization,
|
||||
bool StdInitListInitialization,
|
||||
bool ZeroInitialization,
|
||||
ConstructionKind ConstructKind,
|
||||
SourceRange ParenOrBraceRange)
|
||||
|
@ -861,6 +867,7 @@ CXXConstructExpr::CXXConstructExpr(const ASTContext &C, StmtClass SC,
|
|||
NumArgs(args.size()),
|
||||
Elidable(elidable), HadMultipleCandidates(HadMultipleCandidates),
|
||||
ListInitialization(ListInitialization),
|
||||
StdInitListInitialization(StdInitListInitialization),
|
||||
ZeroInitialization(ZeroInitialization),
|
||||
ConstructKind(ConstructKind), Args(nullptr)
|
||||
{
|
||||
|
|
|
@ -3021,6 +3021,7 @@ CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction(
|
|||
ConstructorArgs,
|
||||
CXXConstExpr->hadMultipleCandidates(),
|
||||
CXXConstExpr->isListInitialization(),
|
||||
CXXConstExpr->isStdInitListInitialization(),
|
||||
CXXConstExpr->requiresZeroInitialization(),
|
||||
CXXConstExpr->getConstructionKind(),
|
||||
SourceRange());
|
||||
|
|
|
@ -10709,6 +10709,7 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
|
|||
MultiExprArg ExprArgs,
|
||||
bool HadMultipleCandidates,
|
||||
bool IsListInitialization,
|
||||
bool IsStdInitListInitialization,
|
||||
bool RequiresZeroInit,
|
||||
unsigned ConstructKind,
|
||||
SourceRange ParenRange) {
|
||||
|
@ -10732,7 +10733,8 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
|
|||
|
||||
return BuildCXXConstructExpr(ConstructLoc, DeclInitType, Constructor,
|
||||
Elidable, ExprArgs, HadMultipleCandidates,
|
||||
IsListInitialization, RequiresZeroInit,
|
||||
IsListInitialization,
|
||||
IsStdInitListInitialization, RequiresZeroInit,
|
||||
ConstructKind, ParenRange);
|
||||
}
|
||||
|
||||
|
@ -10744,13 +10746,15 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
|
|||
MultiExprArg ExprArgs,
|
||||
bool HadMultipleCandidates,
|
||||
bool IsListInitialization,
|
||||
bool IsStdInitListInitialization,
|
||||
bool RequiresZeroInit,
|
||||
unsigned ConstructKind,
|
||||
SourceRange ParenRange) {
|
||||
MarkFunctionReferenced(ConstructLoc, Constructor);
|
||||
return CXXConstructExpr::Create(
|
||||
Context, DeclInitType, ConstructLoc, Constructor, Elidable, ExprArgs,
|
||||
HadMultipleCandidates, IsListInitialization, RequiresZeroInit,
|
||||
HadMultipleCandidates, IsListInitialization, IsStdInitListInitialization,
|
||||
RequiresZeroInit,
|
||||
static_cast<CXXConstructExpr::ConstructionKind>(ConstructKind),
|
||||
ParenRange);
|
||||
}
|
||||
|
|
|
@ -2560,11 +2560,11 @@ static ExprResult BuildCXXCastArgument(Sema &S,
|
|||
InitializedEntity::InitializeTemporary(Ty),
|
||||
Constructor->getAccess());
|
||||
|
||||
ExprResult Result
|
||||
= S.BuildCXXConstructExpr(CastLoc, Ty, cast<CXXConstructorDecl>(Method),
|
||||
ConstructorArgs, HadMultipleCandidates,
|
||||
/*ListInit*/ false, /*ZeroInit*/ false,
|
||||
CXXConstructExpr::CK_Complete, SourceRange());
|
||||
ExprResult Result = S.BuildCXXConstructExpr(
|
||||
CastLoc, Ty, cast<CXXConstructorDecl>(Method),
|
||||
ConstructorArgs, HadMultipleCandidates,
|
||||
/*ListInit*/ false, /*StdInitListInit*/ false, /*ZeroInit*/ false,
|
||||
CXXConstructExpr::CK_Complete, SourceRange());
|
||||
if (Result.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
|
@ -2709,20 +2709,17 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
|
|||
From, /*FIXME:ConstructLoc*/SourceLocation(),
|
||||
ConstructorArgs))
|
||||
return ExprError();
|
||||
return BuildCXXConstructExpr(/*FIXME:ConstructLoc*/SourceLocation(),
|
||||
ToType, SCS.CopyConstructor,
|
||||
ConstructorArgs,
|
||||
/*HadMultipleCandidates*/ false,
|
||||
/*ListInit*/ false, /*ZeroInit*/ false,
|
||||
CXXConstructExpr::CK_Complete,
|
||||
SourceRange());
|
||||
return BuildCXXConstructExpr(
|
||||
/*FIXME:ConstructLoc*/ SourceLocation(), ToType, SCS.CopyConstructor,
|
||||
ConstructorArgs, /*HadMultipleCandidates*/ false,
|
||||
/*ListInit*/ false, /*StdInitListInit*/ false, /*ZeroInit*/ false,
|
||||
CXXConstructExpr::CK_Complete, SourceRange());
|
||||
}
|
||||
return BuildCXXConstructExpr(/*FIXME:ConstructLoc*/SourceLocation(),
|
||||
ToType, SCS.CopyConstructor,
|
||||
From, /*HadMultipleCandidates*/ false,
|
||||
/*ListInit*/ false, /*ZeroInit*/ false,
|
||||
CXXConstructExpr::CK_Complete,
|
||||
SourceRange());
|
||||
return BuildCXXConstructExpr(
|
||||
/*FIXME:ConstructLoc*/ SourceLocation(), ToType, SCS.CopyConstructor,
|
||||
From, /*HadMultipleCandidates*/ false,
|
||||
/*ListInit*/ false, /*StdInitListInit*/ false, /*ZeroInit*/ false,
|
||||
CXXConstructExpr::CK_Complete, SourceRange());
|
||||
}
|
||||
|
||||
// Resolve overloaded function references.
|
||||
|
|
|
@ -2760,7 +2760,6 @@ void InitializationSequence::Step::Destroy() {
|
|||
case SK_QualificationConversionLValue:
|
||||
case SK_LValueToRValue:
|
||||
case SK_ListInitialization:
|
||||
case SK_ListConstructorCall:
|
||||
case SK_UnwrapInitList:
|
||||
case SK_RewrapInitList:
|
||||
case SK_ConstructorInitialization:
|
||||
|
@ -2775,6 +2774,7 @@ void InitializationSequence::Step::Destroy() {
|
|||
case SK_PassByIndirectRestore:
|
||||
case SK_ProduceObjCObject:
|
||||
case SK_StdInitializerList:
|
||||
case SK_StdInitializerListConstructorCall:
|
||||
case SK_OCLSamplerInit:
|
||||
case SK_OCLZeroEvent:
|
||||
break;
|
||||
|
@ -2945,7 +2945,7 @@ InitializationSequence
|
|||
bool HadMultipleCandidates,
|
||||
bool FromInitList, bool AsInitList) {
|
||||
Step S;
|
||||
S.Kind = FromInitList ? AsInitList ? SK_ListConstructorCall
|
||||
S.Kind = FromInitList ? AsInitList ? SK_StdInitializerListConstructorCall
|
||||
: SK_ConstructorInitializationFromList
|
||||
: SK_ConstructorInitialization;
|
||||
S.Type = T;
|
||||
|
@ -5148,6 +5148,7 @@ static ExprResult CopyObject(Sema &S,
|
|||
ConstructorArgs,
|
||||
HadMultipleCandidates,
|
||||
/*ListInit*/ false,
|
||||
/*StdInitListInit*/ false,
|
||||
/*ZeroInit*/ false,
|
||||
CXXConstructExpr::CK_Complete,
|
||||
SourceRange());
|
||||
|
@ -5269,6 +5270,7 @@ PerformConstructorInitialization(Sema &S,
|
|||
const InitializationSequence::Step& Step,
|
||||
bool &ConstructorInitRequiresZeroInit,
|
||||
bool IsListInitialization,
|
||||
bool IsStdInitListInitialization,
|
||||
SourceLocation LBraceLoc,
|
||||
SourceLocation RBraceLoc) {
|
||||
unsigned NumArgs = Args.size();
|
||||
|
@ -5330,7 +5332,7 @@ PerformConstructorInitialization(Sema &S,
|
|||
CurInit = new (S.Context) CXXTemporaryObjectExpr(
|
||||
S.Context, Constructor, TSInfo, ConstructorArgs, ParenOrBraceRange,
|
||||
HadMultipleCandidates, IsListInitialization,
|
||||
ConstructorInitRequiresZeroInit);
|
||||
IsStdInitListInitialization, ConstructorInitRequiresZeroInit);
|
||||
} else {
|
||||
CXXConstructExpr::ConstructionKind ConstructKind =
|
||||
CXXConstructExpr::CK_Complete;
|
||||
|
@ -5359,6 +5361,7 @@ PerformConstructorInitialization(Sema &S,
|
|||
ConstructorArgs,
|
||||
HadMultipleCandidates,
|
||||
IsListInitialization,
|
||||
IsStdInitListInitialization,
|
||||
ConstructorInitRequiresZeroInit,
|
||||
ConstructKind,
|
||||
ParenOrBraceRange);
|
||||
|
@ -5368,6 +5371,7 @@ PerformConstructorInitialization(Sema &S,
|
|||
ConstructorArgs,
|
||||
HadMultipleCandidates,
|
||||
IsListInitialization,
|
||||
IsStdInitListInitialization,
|
||||
ConstructorInitRequiresZeroInit,
|
||||
ConstructKind,
|
||||
ParenOrBraceRange);
|
||||
|
@ -5784,7 +5788,7 @@ InitializationSequence::Perform(Sema &S,
|
|||
|
||||
case SK_ConstructorInitialization:
|
||||
case SK_ConstructorInitializationFromList:
|
||||
case SK_ListConstructorCall:
|
||||
case SK_StdInitializerListConstructorCall:
|
||||
case SK_ZeroInitialization:
|
||||
break;
|
||||
}
|
||||
|
@ -5959,6 +5963,7 @@ InitializationSequence::Perform(Sema &S,
|
|||
ConstructorArgs,
|
||||
HadMultipleCandidates,
|
||||
/*ListInit*/ false,
|
||||
/*StdInitListInit*/ false,
|
||||
/*ZeroInit*/ false,
|
||||
CXXConstructExpr::CK_Complete,
|
||||
SourceRange());
|
||||
|
@ -6132,6 +6137,7 @@ InitializationSequence::Perform(Sema &S,
|
|||
Kind, Arg, *Step,
|
||||
ConstructorInitRequiresZeroInit,
|
||||
/*IsListInitialization*/true,
|
||||
/*IsStdInitListInit*/false,
|
||||
InitList->getLBraceLoc(),
|
||||
InitList->getRBraceLoc());
|
||||
break;
|
||||
|
@ -6154,7 +6160,7 @@ InitializationSequence::Perform(Sema &S,
|
|||
}
|
||||
|
||||
case SK_ConstructorInitialization:
|
||||
case SK_ListConstructorCall: {
|
||||
case SK_StdInitializerListConstructorCall: {
|
||||
// When an initializer list is passed for a parameter of type "reference
|
||||
// to object", we don't get an EK_Temporary entity, but instead an
|
||||
// EK_Parameter entity with reference type.
|
||||
|
@ -6164,10 +6170,13 @@ InitializationSequence::Perform(Sema &S,
|
|||
InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(
|
||||
Entity.getType().getNonReferenceType());
|
||||
bool UseTemporary = Entity.getType()->isReferenceType();
|
||||
bool IsStdInitListInit =
|
||||
Step->Kind == SK_StdInitializerListConstructorCall;
|
||||
CurInit = PerformConstructorInitialization(
|
||||
S, UseTemporary ? TempEntity : Entity, Kind, Args, *Step,
|
||||
ConstructorInitRequiresZeroInit,
|
||||
/*IsListInitialization*/Step->Kind == SK_ListConstructorCall,
|
||||
/*IsListInitialization*/IsStdInitListInit,
|
||||
/*IsStdInitListInitialization*/IsStdInitListInit,
|
||||
/*LBraceLoc*/SourceLocation(),
|
||||
/*RBraceLoc*/SourceLocation());
|
||||
break;
|
||||
|
@ -7030,10 +7039,6 @@ void InitializationSequence::dump(raw_ostream &OS) const {
|
|||
OS << "list initialization via constructor";
|
||||
break;
|
||||
|
||||
case SK_ListConstructorCall:
|
||||
OS << "list initialization via initializer list constructor";
|
||||
break;
|
||||
|
||||
case SK_ZeroInitialization:
|
||||
OS << "zero initialization";
|
||||
break;
|
||||
|
@ -7074,6 +7079,10 @@ void InitializationSequence::dump(raw_ostream &OS) const {
|
|||
OS << "std::initializer_list from initializer list";
|
||||
break;
|
||||
|
||||
case SK_StdInitializerListConstructorCall:
|
||||
OS << "list initialization from std::initializer_list";
|
||||
break;
|
||||
|
||||
case SK_OCLSamplerInit:
|
||||
OS << "OpenCL sampler_t from integer constant";
|
||||
break;
|
||||
|
|
|
@ -2375,6 +2375,7 @@ public:
|
|||
MultiExprArg Args,
|
||||
bool HadMultipleCandidates,
|
||||
bool ListInitialization,
|
||||
bool StdInitListInitialization,
|
||||
bool RequiresZeroInit,
|
||||
CXXConstructExpr::ConstructionKind ConstructKind,
|
||||
SourceRange ParenRange) {
|
||||
|
@ -2387,6 +2388,7 @@ public:
|
|||
ConvertedArgs,
|
||||
HadMultipleCandidates,
|
||||
ListInitialization,
|
||||
StdInitListInitialization,
|
||||
RequiresZeroInit, ConstructKind,
|
||||
ParenRange);
|
||||
}
|
||||
|
@ -2877,6 +2879,11 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init,
|
|||
if (!Construct || isa<CXXTemporaryObjectExpr>(Construct))
|
||||
return getDerived().TransformExpr(Init);
|
||||
|
||||
// If the initialization implicitly converted an initializer list to a
|
||||
// std::initializer_list object, unwrap the std::initializer_list too.
|
||||
if (Construct && Construct->isStdInitListInitialization())
|
||||
return TransformInitializer(Construct->getArg(0), CXXDirectInit);
|
||||
|
||||
SmallVector<Expr*, 8> NewArgs;
|
||||
bool ArgChanged = false;
|
||||
if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(),
|
||||
|
@ -8571,6 +8578,7 @@ TreeTransform<Derived>::TransformCXXConstructExpr(CXXConstructExpr *E) {
|
|||
Args,
|
||||
E->hadMultipleCandidates(),
|
||||
E->isListInitialization(),
|
||||
E->isStdInitListInitialization(),
|
||||
E->requiresZeroInitialization(),
|
||||
E->getConstructionKind(),
|
||||
E->getParenOrBraceRange());
|
||||
|
|
|
@ -1196,6 +1196,7 @@ void ASTStmtReader::VisitCXXConstructExpr(CXXConstructExpr *E) {
|
|||
E->setElidable(Record[Idx++]);
|
||||
E->setHadMultipleCandidates(Record[Idx++]);
|
||||
E->setListInitialization(Record[Idx++]);
|
||||
E->setStdInitListInitialization(Record[Idx++]);
|
||||
E->setRequiresZeroInitialization(Record[Idx++]);
|
||||
E->setConstructionKind((CXXConstructExpr::ConstructionKind)Record[Idx++]);
|
||||
E->ParenOrBraceRange = ReadSourceRange(Record, Idx);
|
||||
|
|
|
@ -1150,6 +1150,7 @@ void ASTStmtWriter::VisitCXXConstructExpr(CXXConstructExpr *E) {
|
|||
Record.push_back(E->isElidable());
|
||||
Record.push_back(E->hadMultipleCandidates());
|
||||
Record.push_back(E->isListInitialization());
|
||||
Record.push_back(E->isStdInitListInitialization());
|
||||
Record.push_back(E->requiresZeroInitialization());
|
||||
Record.push_back(E->getConstructionKind()); // FIXME: stable encoding
|
||||
Writer.AddSourceRange(E->getParenOrBraceRange(), Record);
|
||||
|
|
|
@ -242,6 +242,7 @@ namespace DR1070 {
|
|||
namespace ListInitInstantiate {
|
||||
struct A {
|
||||
A(std::initializer_list<A>);
|
||||
A(std::initializer_list<int>);
|
||||
};
|
||||
struct B : A {
|
||||
B(int);
|
||||
|
@ -253,4 +254,8 @@ namespace ListInitInstantiate {
|
|||
template<typename T> X<T>::X() : a{B{0}, B{1}} {}
|
||||
|
||||
X<int> x;
|
||||
|
||||
int f(const A&);
|
||||
template<typename T> void g() { int k = f({0}); }
|
||||
template void g<int>();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue