Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading the End of Recursion for a Variable Length Template Function

François Andrieux gave me a good workaround for this Visual Studio 2017 problem. I was trying to build on his answer like so:

template<class T, size_t N>
ostream& vector_insert_impl(ostream& lhs, const char*, const T& rhs)
{
    return lhs << at(rhs, N);
}

template<class T, size_t N, size_t... I>
ostream& vector_insert_impl(ostream& lhs, const char* delim, const T& rhs)
{
    return vector_insert_impl<T, I...>(lhs << at(rhs, N) << delim, delim, rhs);
}

template <typename T, size_t... I>
ostream& vector_insert(ostream& lhs, const char* delim, const T& rhs, index_sequence<I...>) 
{
    return vector_insert_impl<T, I...>(it, delim, rhs);
}

The key difference is that the "end of recursion" templated function actually inserts the last value into the ostream, and not the delimiter rather than being a no-op. But when I try to compile this I get the error:

error C2668: vector_insert_impl: ambiguous call to overloaded function (compiling source file ....\src\STETestbed\Test.cpp)
note: could be std::ostream &vector_insert_impl<T,2,>(std::ostream &,const char *,const T &)
note: or std::ostream &vector_insert_impl<T,2>(std::ostream &,const char *,const T &)

I thought variable length template functions were considered 3rd class citizens and fixed length template functions would always be preferred. That preference doesn't appear to be in effect here. Is there a workaround which will force the compiler to choose my "end of recursion" function enabling me to avoid inserting the delimiter?

like image 558
Jonathan Mee Avatar asked Oct 15 '22 13:10

Jonathan Mee


1 Answers

Is there a workaround which will force the compiler to choose my "end of recursion" function enabling me to avoid inserting the delimiter?

You can add the "Next" element

template <typename T, std::size_t N>
std::ostream & vector_insert_impl (std::ostream & lhs, char const *, T const & rhs)
{
    return lhs << at(rhs, N);
}

// ..................................vvvvvvvvvvvvvvvv
template <typename T, std::size_t N, std::size_t Next, std::size_t ... I>
std::ostream & vector_insert_impl (std::ostream & lhs, char const * delim, T const & rhs)
{ // ............................vvvv
    return vector_insert_impl<T, Next, I...>(lhs << at(rhs, N) << delim, delim, rhs);
}

but, if you can use C++17, I suppose if constexpr is a better solution

template <typename T, std::size_t N, std::size_t ... Is>
std::ostream & vector_insert_impl (std::ostream & lhs, char const * delim, T const & rhs)
{
   if constexpr ( sizeof...(Is) )
      return vector_insert_impl<T, Is...>(lhs << at(rhs, N) << delim, delim, rhs);
   else
      return lhs << at(rhs, N);
}
like image 60
max66 Avatar answered Nov 15 '22 10:11

max66