重载可变长度模板函数的递归结束

Overloading the End of Recursion for a Variable Length Template Function

François Andrieux gave me a good workaround for 。我试图以他的回答为基础:

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);
}

主要区别在于 "end of recursion" 模板化函数实际上将最后一个值插入到 ostream 而不是定界符 而不是 no -操作。但是当我尝试编译它时出现错误:

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

我认为可变长度模板函数被认为是 3rd class 公民,固定长度模板函数总是首选。这种偏好在这里似乎没有生效。是否有一种解决方法可以强制编译器选择我的 "end of recursion" 函数,从而避免插入定界符?

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

您可以添加“Next”元素

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);
}

但是,如果你可以使用 C++17,我想 if constexpr 是更好的解决方案

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);
}

避免模棱两可的调用的最简单解决方案是向第二个重载添加额外的 size_t。这样它只能用至少 2 个参数调用,而 1 个参数的情况将归结为你的第一个重载。

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

可变长度模板函数不被视为第三 class 公民。当你有

template<class T, size_t N>
ostream& vector_insert_impl(ostream& lhs, const char*, const T& rhs)

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

然后当您还剩 1 个值时,您可以在第一个函数和最后一个带有空包的函数之间进行选择。两者都没有偏好,所以你有一个模棱两可的电话。他们解决此问题的方法是将其设置为如果包为空则无法调用可变大小写的位置。您可以通过添加第二个 non-type 参数来做到这一点,这样只有在有 2 个或更多值如

时才会调用它
template<class T, size_t N, size_t M, size_t... I>
ostream& vector_insert_impl(ostream& lhs, const char* delim, const T& rhs)
{
    return vector_insert_impl<T, M, I...>(lhs << at(rhs, N) << delim, delim, rhs);
}

所以现在当 I 为空时,您有 vector_insert_impl<T, M> 被调用,唯一有效的重载是第一个。


什么是第三 class 公民是旧的 C 风格可变参数函数。 void foo(int, ...)不会和void foo(int, int).

产生歧义