aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/Decl.h37
-rw-r--r--include/clang/AST/DeclTemplate.h47
-rw-r--r--lib/AST/Decl.cpp35
-rw-r--r--lib/CodeGen/CodeGenModule.cpp5
-rw-r--r--lib/Sema/Sema.h11
-rw-r--r--lib/Sema/SemaTemplate.cpp20
-rw-r--r--lib/Sema/SemaTemplateInstantiate.cpp38
-rw-r--r--lib/Sema/SemaTemplateInstantiateDecl.cpp17
-rw-r--r--lib/Sema/SemaType.cpp4
-rw-r--r--test/CodeGenCXX/explicit-instantiation.cpp12
10 files changed, 139 insertions, 87 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 1d6c9c1872..28456fff9f 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -715,6 +715,29 @@ public:
static bool classof(const OriginalParmVarDecl *D) { return true; }
};
+// \brief Describes the kind of template specialization that a
+// particular template specialization declaration represents.
+enum TemplateSpecializationKind {
+ /// This template specialization was formed from a template-id but
+ /// has not yet been declared, defined, or instantiated.
+ TSK_Undeclared = 0,
+ /// This template specialization was implicitly instantiated from a
+ /// template. (C++ [temp.inst]).
+ TSK_ImplicitInstantiation,
+ /// This template specialization was declared or defined by an
+ /// explicit specialization (C++ [temp.expl.spec]) or partial
+ /// specialization (C++ [temp.class.spec]).
+ TSK_ExplicitSpecialization,
+ /// This template specialization was instantiated from a template
+ /// due to an explicit instantiation declaration request
+ /// (C++0x [temp.explicit]).
+ TSK_ExplicitInstantiationDeclaration,
+ /// This template specialization was instantiated from a template
+ /// due to an explicit instantiation definition request
+ /// (C++ [temp.explicit]).
+ TSK_ExplicitInstantiationDefinition
+};
+
/// FunctionDecl - An instance of this class is created to represent a
/// function declaration or definition.
///
@@ -1077,14 +1100,14 @@ public:
const TemplateArgumentList *TemplateArgs,
void *InsertPos);
- /// \brief Determine whether this is an explicit specialization of a
- /// function template or a member function of a class template.
- bool isExplicitSpecialization() const;
+ /// \brief Determine what kind of template instantiation this function
+ /// represents.
+ TemplateSpecializationKind getTemplateSpecializationKind() const;
- /// \brief Note that this is an explicit specialization of a function template
- /// or a member function of a class template.
- void setExplicitSpecialization(bool ES);
-
+ /// \brief Determine what kind of template instantiation this function
+ /// represents.
+ void setTemplateSpecializationKind(TemplateSpecializationKind TSK);
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() >= FunctionFirst && D->getKind() <= FunctionLast;
diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h
index 971d6ea07b..24524dff38 100644
--- a/include/clang/AST/DeclTemplate.h
+++ b/include/clang/AST/DeclTemplate.h
@@ -510,9 +510,8 @@ public:
/// \brief The function template from which this function template
/// specialization was generated.
///
- /// The bit will be 0 for an implicit instantiation, 1 for an explicit
- /// specialization.
- llvm::PointerIntPair<FunctionTemplateDecl *, 1> Template;
+ /// The two bits are contain the top 4 values of TemplateSpecializationKind.
+ llvm::PointerIntPair<FunctionTemplateDecl *, 2> Template;
/// \brief The template arguments used to produce the function template
/// specialization from the function template.
@@ -520,14 +519,17 @@ public:
/// \brief Retrieve the template from which this function was specialized.
FunctionTemplateDecl *getTemplate() const { return Template.getPointer(); }
-
- /// \brief Determine whether this is an explicit specialization.
- bool isExplicitSpecialization() const { return Template.getInt(); }
-
- /// \brief Set whether this is an explicit specialization or an implicit
- /// instantiation.
- void setExplicitSpecialization(bool ES) {
- Template.setInt(ES);
+
+ /// \brief Determine what kind of template specialization this is.
+ TemplateSpecializationKind getTemplateSpecializationKind() const {
+ return (TemplateSpecializationKind)(Template.getInt() + 1);
+ }
+
+ /// \brief Set the template specialization kind.
+ void setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
+ assert(TSK != TSK_Undeclared &&
+ "Cannot encode TSK_Undeclared for a function template specialization");
+ Template.setInt(TSK - 1);
}
void Profile(llvm::FoldingSetNodeID &ID) {
@@ -871,24 +873,6 @@ public:
static bool classof(const TemplateTemplateParmDecl *D) { return true; }
};
-// \brief Describes the kind of template specialization that a
-// particular template specialization declaration represents.
-enum TemplateSpecializationKind {
- /// This template specialization was formed from a template-id but
- /// has not yet been declared, defined, or instantiated.
- TSK_Undeclared = 0,
- /// This template specialization was declared or defined by an
- /// explicit specialization (C++ [temp.expl.spec]) or partial
- /// specialization (C++ [temp.class.spec]).
- TSK_ExplicitSpecialization,
- /// This template specialization was implicitly instantiated from a
- /// template. (C++ [temp.inst]).
- TSK_ImplicitInstantiation,
- /// This template specialization was instantiated from a template
- /// due to an explicit instantiation request (C++ [temp.explicit]).
- TSK_ExplicitInstantiation
-};
-
/// \brief Represents a class template specialization, which refers to
/// a class template with a given set of template arguments.
///
@@ -927,7 +911,7 @@ class ClassTemplateSpecializationDecl
/// \brief The kind of specialization this declaration refers to.
/// Really a value of type TemplateSpecializationKind.
- unsigned SpecializationKind : 2;
+ unsigned SpecializationKind : 3;
protected:
ClassTemplateSpecializationDecl(ASTContext &Context, Kind DK,
@@ -972,7 +956,8 @@ public:
ClassTemplatePartialSpecializationDecl *>
getInstantiatedFrom() const {
if (getSpecializationKind() != TSK_ImplicitInstantiation &&
- getSpecializationKind() != TSK_ExplicitInstantiation)
+ getSpecializationKind() != TSK_ExplicitInstantiationDefinition &&
+ getSpecializationKind() != TSK_ExplicitInstantiationDeclaration)
return (ClassTemplateDecl*)0;
if (SpecializedPartialSpecialization *PartialSpec
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 41ba1f1df9..ca1ae63ff8 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -640,7 +640,7 @@ FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context,
Info->Function = this;
Info->Template.setPointer(Template);
- Info->Template.setInt(0); // Implicit instantiation, unless told otherwise
+ Info->Template.setInt(TSK_ImplicitInstantiation - 1);
Info->TemplateArguments = TemplateArgs;
TemplateOrSpecialization = Info;
@@ -649,24 +649,35 @@ FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context,
Template->getSpecializations().InsertNode(Info, InsertPos);
}
-bool FunctionDecl::isExplicitSpecialization() const {
- // FIXME: check this property for explicit specializations of member
- // functions of class templates.
+TemplateSpecializationKind FunctionDecl::getTemplateSpecializationKind() const {
+ // For a function template specialization, query the specialization
+ // information object.
FunctionTemplateSpecializationInfo *Info
= TemplateOrSpecialization.dyn_cast<FunctionTemplateSpecializationInfo*>();
- if (!Info)
- return false;
+ if (Info)
+ return Info->getTemplateSpecializationKind();
+
+ if (!getInstantiatedFromMemberFunction())
+ return TSK_Undeclared;
- return Info->isExplicitSpecialization();
+ // Find the class template specialization corresponding to this instantiation
+ // of a member function.
+ const DeclContext *Parent = getDeclContext();
+ while (Parent && !isa<ClassTemplateSpecializationDecl>(Parent))
+ Parent = Parent->getParent();
+
+ if (!Parent)
+ return TSK_Undeclared;
+
+ return cast<ClassTemplateSpecializationDecl>(Parent)->getSpecializationKind();
}
-void FunctionDecl::setExplicitSpecialization(bool ES) {
- // FIXME: set this property for explicit specializations of member functions
- // of class templates.
+void
+FunctionDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
FunctionTemplateSpecializationInfo *Info
= TemplateOrSpecialization.dyn_cast<FunctionTemplateSpecializationInfo*>();
- if (Info)
- Info->setExplicitSpecialization(ES);
+ assert(Info && "Not a function template specialization");
+ Info->setTemplateSpecializationKind(TSK);
}
//===----------------------------------------------------------------------===//
diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp
index cde6e89d66..180a68659d 100644
--- a/lib/CodeGen/CodeGenModule.cpp
+++ b/lib/CodeGen/CodeGenModule.cpp
@@ -250,9 +250,8 @@ GetLinkageForFunction(ASTContext &Context, const FunctionDecl *FD,
// The kind of external linkage this function will have, if it is not
// inline or static.
CodeGenModule::GVALinkage External = CodeGenModule::GVA_StrongExternal;
- if (Context.getLangOptions().CPlusPlus &&
- (FD->getPrimaryTemplate() || FD->getInstantiatedFromMemberFunction()) &&
- !FD->isExplicitSpecialization())
+ if (Context.getLangOptions().CPlusPlus &&
+ FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
External = CodeGenModule::GVA_TemplateInstantiation;
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index d088387b05..1c118ab64e 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -3011,21 +3011,24 @@ public:
InstantiateClass(SourceLocation PointOfInstantiation,
CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern,
const MultiLevelTemplateArgumentList &TemplateArgs,
- bool ExplicitInstantiation,
+ TemplateSpecializationKind TSK,
bool Complain = true);
bool
InstantiateClassTemplateSpecialization(
ClassTemplateSpecializationDecl *ClassTemplateSpec,
- bool ExplicitInstantiation, bool Complain = true);
+ TemplateSpecializationKind TSK,
+ bool Complain = true);
void InstantiateClassMembers(SourceLocation PointOfInstantiation,
CXXRecordDecl *Instantiation,
- const MultiLevelTemplateArgumentList &TemplateArgs);
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ TemplateSpecializationKind TSK);
void InstantiateClassTemplateSpecializationMembers(
SourceLocation PointOfInstantiation,
- ClassTemplateSpecializationDecl *ClassTemplateSpec);
+ ClassTemplateSpecializationDecl *ClassTemplateSpec,
+ TemplateSpecializationKind TSK);
NestedNameSpecifier *
SubstNestedNameSpecifier(NestedNameSpecifier *NNS,
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 76b6d7ecaa..c243765bec 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -2934,7 +2934,8 @@ Sema::ActOnExplicitInstantiation(Scope *S,
bool SpecializationRequiresInstantiation = true;
if (PrevDecl) {
- if (PrevDecl->getSpecializationKind() == TSK_ExplicitInstantiation) {
+ if (PrevDecl->getSpecializationKind()
+ == TSK_ExplicitInstantiationDefinition) {
// This particular specialization has already been declared or
// instantiated. We cannot explicitly instantiate it.
Diag(TemplateNameLoc, diag::err_explicit_instantiation_duplicate)
@@ -3028,16 +3029,19 @@ Sema::ActOnExplicitInstantiation(Scope *S,
//
// This check comes when we actually try to perform the
// instantiation.
+ TemplateSpecializationKind TSK
+ = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
+ : TSK_ExplicitInstantiationDeclaration;
if (SpecializationRequiresInstantiation)
- InstantiateClassTemplateSpecialization(Specialization, true);
+ InstantiateClassTemplateSpecialization(Specialization, TSK);
else // Instantiate the members of this class template specialization.
- InstantiateClassTemplateSpecializationMembers(TemplateLoc, Specialization);
+ InstantiateClassTemplateSpecializationMembers(TemplateLoc, Specialization,
+ TSK);
return DeclPtrTy::make(Specialization);
}
// Explicit instantiation of a member class of a class template.
-// FIXME: Implement extern template semantics.
Sema::DeclResult
Sema::ActOnExplicitInstantiation(Scope *S,
SourceLocation ExternLoc,
@@ -3092,17 +3096,21 @@ Sema::ActOnExplicitInstantiation(Scope *S,
}
}
+ TemplateSpecializationKind TSK
+ = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
+ : TSK_ExplicitInstantiationDeclaration;
+
if (!Record->getDefinition(Context)) {
// If the class has a definition, instantiate it (and all of its
// members, recursively).
Pattern = cast_or_null<CXXRecordDecl>(Pattern->getDefinition(Context));
if (Pattern && InstantiateClass(TemplateLoc, Record, Pattern,
getTemplateInstantiationArgs(Record),
- /*ExplicitInstantiation=*/true))
+ TSK))
return true;
} else // Instantiate all of the members of the class.
InstantiateClassMembers(TemplateLoc, Record,
- getTemplateInstantiationArgs(Record));
+ getTemplateInstantiationArgs(Record), TSK);
// FIXME: We don't have any representation for explicit instantiations of
// member classes. Such a representation is not needed for compilation, but it
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 0c47e996c8..55e81aecbc 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -623,8 +623,7 @@ Sema::SubstBaseSpecifiers(CXXRecordDecl *Instantiation,
/// \param TemplateArgs The template arguments to be substituted into
/// the pattern.
///
-/// \param ExplicitInstantiation whether this is an explicit instantiation
-/// (otherwise, it is an implicit instantiation).
+/// \param TSK the kind of implicit or explicit instantiation to perform.
///
/// \param Complain whether to complain if the class cannot be instantiated due
/// to the lack of a definition.
@@ -634,7 +633,7 @@ bool
Sema::InstantiateClass(SourceLocation PointOfInstantiation,
CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern,
const MultiLevelTemplateArgumentList &TemplateArgs,
- bool ExplicitInstantiation,
+ TemplateSpecializationKind TSK,
bool Complain) {
bool Invalid = false;
@@ -650,7 +649,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
Diag(Pattern->getLocation(), diag::note_member_of_template_here);
} else {
Diag(PointOfInstantiation, diag::err_template_instantiate_undefined)
- << ExplicitInstantiation
+ << (TSK != TSK_ImplicitInstantiation)
<< Context.getTypeDeclType(Instantiation);
Diag(Pattern->getLocation(), diag::note_template_decl_here);
}
@@ -708,9 +707,10 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
Consumer.HandleTagDeclDefinition(Instantiation);
// If this is an explicit instantiation, instantiate our members, too.
- if (!Invalid && ExplicitInstantiation) {
+ if (!Invalid && TSK != TSK_ImplicitInstantiation) {
Inst.Clear();
- InstantiateClassMembers(PointOfInstantiation, Instantiation, TemplateArgs);
+ InstantiateClassMembers(PointOfInstantiation, Instantiation, TemplateArgs,
+ TSK);
}
return Invalid;
@@ -719,7 +719,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
bool
Sema::InstantiateClassTemplateSpecialization(
ClassTemplateSpecializationDecl *ClassTemplateSpec,
- bool ExplicitInstantiation,
+ TemplateSpecializationKind TSK,
bool Complain) {
// Perform the actual instantiation on the canonical declaration.
ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>(
@@ -798,14 +798,12 @@ Sema::InstantiateClassTemplateSpecialization(
}
// Note that this is an instantiation.
- ClassTemplateSpec->setSpecializationKind(
- ExplicitInstantiation? TSK_ExplicitInstantiation
- : TSK_ImplicitInstantiation);
+ ClassTemplateSpec->setSpecializationKind(TSK);
bool Result = InstantiateClass(ClassTemplateSpec->getLocation(),
ClassTemplateSpec, Pattern,
getTemplateInstantiationArgs(ClassTemplateSpec),
- ExplicitInstantiation,
+ TSK,
Complain);
for (unsigned I = 0, N = Matched.size(); I != N; ++I) {
@@ -822,8 +820,10 @@ Sema::InstantiateClassTemplateSpecialization(
/// or a member class of a template.
void
Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
- CXXRecordDecl *Instantiation,
- const MultiLevelTemplateArgumentList &TemplateArgs) {
+ CXXRecordDecl *Instantiation,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ TemplateSpecializationKind TSK) {
+ // FIXME: extern templates
for (DeclContext::decl_iterator D = Instantiation->decls_begin(),
DEnd = Instantiation->decls_end();
D != DEnd; ++D) {
@@ -839,7 +839,8 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
"Missing instantiated-from-template information");
InstantiateClass(PointOfInstantiation, Record,
Record->getInstantiatedFromMemberClass(),
- TemplateArgs, true);
+ TemplateArgs,
+ TSK);
}
}
}
@@ -848,9 +849,11 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
/// \brief Instantiate the definitions of all of the members of the
/// given class template specialization, which was named as part of an
/// explicit instantiation.
-void Sema::InstantiateClassTemplateSpecializationMembers(
+void
+Sema::InstantiateClassTemplateSpecializationMembers(
SourceLocation PointOfInstantiation,
- ClassTemplateSpecializationDecl *ClassTemplateSpec) {
+ ClassTemplateSpecializationDecl *ClassTemplateSpec,
+ TemplateSpecializationKind TSK) {
// C++0x [temp.explicit]p7:
// An explicit instantiation that names a class template
// specialization is an explicit instantion of the same kind
@@ -860,7 +863,8 @@ void Sema::InstantiateClassTemplateSpecializationMembers(
// containing the explicit instantiation, except as described
// below.
InstantiateClassMembers(PointOfInstantiation, ClassTemplateSpec,
- getTemplateInstantiationArgs(ClassTemplateSpec));
+ getTemplateInstantiationArgs(ClassTemplateSpec),
+ TSK);
}
Sema::OwningStmtResult
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 807115eb67..b63fc400a5 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -402,6 +402,8 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
Decl *
TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
+ // FIXME: Dig out the out-of-line definition of this function template?
+
TemplateParameterList *TempParams = D->getTemplateParameters();
TemplateParameterList *InstParams = SubstTemplateParams(TempParams);
if (!InstParams)
@@ -496,7 +498,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
D->getStorageClass(),
D->isInline(), D->hasWrittenPrototype());
Function->setLexicalDeclContext(Owner);
-
+
// Attach the parameters
for (unsigned P = 0; P < Params.size(); ++P)
Params[P]->setOwningFunction(Function);
@@ -622,7 +624,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
Method->getDeclName(),
TemplateParams, Method);
if (D->isOutOfLine())
- FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext());
+ FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext());
Method->setDescribedFunctionTemplate(FunctionTemplate);
} else if (!FunctionTemplate)
Method->setInstantiationOfMemberFunction(D);
@@ -979,6 +981,15 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
if (!Pattern)
return;
+ // C++0x [temp.explicit]p9:
+ // Except for inline functions, other explicit instantiation declarations
+ // have the effect of suppressing the implicit instantiation of the entity
+ // to which they refer.
+ if (Function->getTemplateSpecializationKind()
+ == TSK_ExplicitInstantiationDeclaration &&
+ PatternDecl->isOutOfLine() && !PatternDecl->isInline())
+ return;
+
InstantiatingTemplate Inst(*this, PointOfInstantiation, Function);
if (Inst)
return;
@@ -1080,6 +1091,8 @@ void Sema::InstantiateStaticDataMemberDefinition(
return;
}
+ // FIXME: extern templates
+
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
if (Inst)
return;
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 11bbe4e2de..80cdcd5464 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -1781,7 +1781,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
if (Loc.isValid())
ClassTemplateSpec->setLocation(Loc);
return InstantiateClassTemplateSpecialization(ClassTemplateSpec,
- /*ExplicitInstantiation=*/false,
+ TSK_ImplicitInstantiation,
/*Complain=*/diag != 0);
}
} else if (CXXRecordDecl *Rec
@@ -1790,7 +1790,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
// This record was instantiated from a class within a template.
return InstantiateClass(Loc, Rec, Pattern,
getTemplateInstantiationArgs(Rec),
- /*ExplicitInstantiation=*/false,
+ TSK_ImplicitInstantiation,
/*Complain=*/diag != 0);
}
}
diff --git a/test/CodeGenCXX/explicit-instantiation.cpp b/test/CodeGenCXX/explicit-instantiation.cpp
index 38966aad2d..33cbf7f872 100644
--- a/test/CodeGenCXX/explicit-instantiation.cpp
+++ b/test/CodeGenCXX/explicit-instantiation.cpp
@@ -1,11 +1,17 @@
// RUN: clang-cc -emit-llvm -femit-all-decls -o %t %s &&
// RUN: grep "_ZNK4plusIillEclERKiRKl" %t | count 1
+// FIXME: We should not need the -femit-all-decls, because operator() should
+// be emitted as an external symbol rather than with linkonce_odr linkage.
+// This is a Sema problem.
template<typename T, typename U, typename Result>
struct plus {
- Result operator()(const T& t, const U& u) const {
- return t + u;
- }
+ Result operator()(const T& t, const U& u) const;
};
+template<typename T, typename U, typename Result>
+Result plus<T, U, Result>::operator()(const T& t, const U& u) const {
+ return t + u;
+}
+
template struct plus<int, long, long>;