Look at this code:
#include <utility>
template <typename T>
class A
{
public:
A(...) {}
};
template <typename T>
auto Func(A<T> &&a)
-> void
{
}
template <typename T>
auto Func(A<T> &a)
-> decltype( Func<T>(std::move(a)) )
{ return ( Func<T>(std::move(a)) ); }
int main()
{
Func<int>(5);
}
The second overload requests decltype
that should return "return type" of the first overload (that is void
).
But when I try to compile this code with GCC, it says that template instantiation depth exceeds maximum:
main.cpp: In substitution of 'template<class T> decltype (Func<T>(std::move(a))) Func(A<T>&) [with T = int]':
main.cpp:17:23: recursively required by substitution of 'template<class T> decltype (Func<T>(std::move(a))) Func(A<T>&) [with T = int]'
main.cpp:17:23: required by substitution of 'template<class T> decltype (Func<T>(std::move(a))) Func(A<T>&) [with T = int]'
main.cpp:22:14: required from here
main.cpp:17:33: fatal error: template instantiation depth exceeds maximum of 900 (use '-ftemplate-depth=' to increase the maximum)
17 | -> decltype( Func<T>(std::move(a)) )
| ~~~~~~~~~^~~
When I try to compile this code with CLang, it just crashes:
#0 0x00007f0d4efc7eea llvm::sys::PrintStackTrace(llvm::raw_ostream&) (/usr/lib/x86_64-linux-gnu/libLLVM-5.0.so.1+0x7fbeea)
#1 0x00007f0d4efc606e llvm::sys::RunSignalHandlers() (/usr/lib/x86_64-linux-gnu/libLLVM-5.0.so.1+0x7fa06e)
#2 0x00007f0d4efc61bc (/usr/lib/x86_64-linux-gnu/libLLVM-5.0.so.1+0x7fa1bc)
#3 0x00007f0d51d9d390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390)
#4 0x00007f0d4ef6311e llvm::FoldingSetNodeID::ComputeHash() const (/usr/lib/x86_64-linux-gnu/libLLVM-5.0.so.1+0x79711e)
#5 0x00007f0d4ef6318a llvm::FoldingSetBase::FindNodeOrInsertPos(llvm::FoldingSetNodeID const&, void*&) (/usr/lib/x86_64-linux-gnu/libLLVM-5.0.so.1+0x79718a)
#6 0x0000000001838633 clang::ASTContext::getLValueReferenceType(clang::QualType, bool) const (/usr/lib/llvm-5.0/bin/clang+0x1838633)
#7 0x00000000013dee9e (/usr/lib/llvm-5.0/bin/clang+0x13dee9e)
#8 0x00000000013e0998 clang::Sema::DeduceTemplateArguments(clang::FunctionTemplateDecl*, clang::TemplateArgumentListInfo*, llvm::ArrayRef<clang::Expr*>, clang::FunctionDecl*&, clang::sema::TemplateDeductionInfo&, bool, llvm::function_ref<bool (llvm::ArrayRef<clang::QualType>)>) (/usr/lib/llvm-5.0/bin/clang+0x13e0998)
#9 0x000000000131d398 clang::Sema::AddTemplateOverloadCandidate(clang::FunctionTemplateDecl*, clang::DeclAccessPair, clang::TemplateArgumentListInfo*, llvm::ArrayRef<clang::Expr*>, clang::OverloadCandidateSet&, bool, bool) (/usr/lib/llvm-5.0/bin/clang+0x131d398)
#10 0x000000000131e0d5 (/usr/lib/llvm-5.0/bin/clang+0x131e0d5)
#11 0x000000000131e22b clang::Sema::AddOverloadedCallCandidates(clang::UnresolvedLookupExpr*, llvm::ArrayRef<clang::Expr*>, clang::OverloadCandidateSet&, bool) (/usr/lib/llvm-5.0/bin/clang+0x131e22b)
#12 0x000000000131e3da clang::Sema::buildOverloadedCallSet(clang::Scope*, clang::Expr*, clang::UnresolvedLookupExpr*, llvm::MutableArrayRef<clang::Expr*>, clang::SourceLocation, clang::OverloadCandidateSet*, clang::ActionResult<clang::Expr*, true>*) (/usr/lib/llvm-5.0/bin/clang+0x131e3da)
#13 0x000000000132a6a3 clang::Sema::BuildOverloadedCallExpr(clang::Scope*, clang::Expr*, clang::UnresolvedLookupExpr*, clang::SourceLocation, llvm::MutableArrayRef<clang::Expr*>, clang::SourceLocation, clang::Expr*, bool, bool) (/usr/lib/llvm-5.0/bin/clang+0x132a6a3)
#14 0x00000000011d1e04 clang::Sema::ActOnCallExpr(clang::Scope*, clang::Expr*, clang::SourceLocation, llvm::MutableArrayRef<clang::Expr*>, clang::SourceLocation, clang::Expr*, bool) (/usr/lib/llvm-5.0/bin/clang+0x11d1e04)
#15 0x000000000140679c (/usr/lib/llvm-5.0/bin/clang+0x140679c)
#16 0x00000000013f5b2e (/usr/lib/llvm-5.0/bin/clang+0x13f5b2e)
#17 0x00000000013ff848 (/usr/lib/llvm-5.0/bin/clang+0x13ff848)
...
What's wrong with this code? How can I fix it?
The story
What I actually want is to make a function that has the same implementation for both A<T> &&
and A<T> &
parameters. I want to implement one overload, and in the second overload I just want to redirect the call to the first overload. If my code is not valid, how can I do this another way?
In your case is actually the trailing return type which triggers the infinite recursion
-> decltype(Func<T>(std::move(a)))
You can read more about why this happens in this post.
If you are working with C++14 and later just leave it out and it should work: Try it here!