Search code examples
c++templatesoverload-resolution

Why does infinite recursion in template instantiation happen when using trailing return type with decltype that calls the function with same name?


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)) )
      |                        ~~~~~~~~~^~~

coliru

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)
...

coliru

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?


Solution

  • 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!