考虑以下代码
template <int INDEX>
void foo() { } // termination version
template <int INDEX, typename Arg, typename... Args>
void foo(Arg head, Args... args) {
if (INDEX == 0) {
cout << head << endl;
}
else {
foo <INDEX-1 > (args...);
}
}
int main() {
foo<1> (1, 3.1415);
return 0;
}
代码按预期编译并输出3.1415。
但是,以下简单代码编译正常但始终输出1.您对此有任何修复吗?
template <int INDEX>
void foo() { } // termination version
template <int INDEX, typename Arg, typename... Args>
Arg foo(Arg head, Args... args) {
if (INDEX == 0) {
return head;
}
else {
foo <INDEX-1 > (args...);
}
}
int main() {
cout<<foo<1> (1, 3.1415,"Test!");
return 0;
}
换句话说,如何递归调用具有不同参数类型的可变参数模板化函数?
答案 0 :(得分:1)
return
foo<1>
确保您了解嵌套调用中return
的工作原理。您的foo<1>
来电foo<0>
会将其(foo<0>
&#39; s)的第一个参数返回foo<1>
。但是,foo<1>
并不关心foo<0>
的回复,因为它会像foo<0>
这样调用:
else {
foo<i-1>(args...);// `i-1` becomes `0`
}
编译器知道你在这里遇到问题:从foo<1>
(已被忽略)返回后,foo<0>
应返回哪个值?它必须返回与第一个参数相同类型的值,但它在到达其结束}
之前永远不会返回。
正如评论中指出的那样,您应该打开编译器警告来检测这些问题。在这种情况下,-Wall
(GCC documentation on warning options)为sufficient for GCC and clang to warn you (online demo),但有更多警告可用。如果您的文件名为main.cpp
,并且在第23行第1列中找到结束}
,则编译器警告可以读取
main.cpp: In function ‘Arg foo(Arg, Args ...) [with int INDEX = 1; Arg = int; Args = {double, const char*}]’:
main.cpp:23:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
您可以尝试通过将foo<0>
的返回值传递给堆栈来修复代码:
else {
return foo<i-1>(args...);// NOTE: type of return value depends on `foo<i-1>`
}
但是,失败是因为foo<1>
已声明返回与第一个参数相同类型的值:
template<int i, class Arg, class... Args>
Arg foo(Arg, Args... args) {// <--------- NOTE: must return a value of type `Arg`
使用C ++ 17,您可以使用auto
作为返回类型和constexpr if
来实现递归,如下所示:
template<size_t i, class T0, class... Ts>
auto foo(T0 v0, Ts... vs) {
static_assert(i < 1u + sizeof...(Ts));
if constexpr(0u == i) return v0;// <------ NOTE: must be `if constexpr` (C++17)
else return foo<i-1u>(vs...);
}
使用C ++ 14,您还可以使用auto
作为返回类型,但constexpr if
不可用。解决方法是一个众所周知的习惯用法,并使用类模板的专门化来实现&#34;实现&#34;递归逻辑:
template<int i>
struct foo_impl {
static_assert(i > 0, "the case `i == 0` requires a specialization");
template<class T0, class... Ts>
static auto get(T0, Ts... vs) {
return foo_impl<i-1>::get(vs...);
}
};
template<>
struct foo_impl<0> {
template<class T0, class... Ts>
static auto get(T0 v0, Ts...) {
return v0;
}
};
template<int i, class... Ts>
auto foo(Ts... vs) {
static_assert(i >= 0 && i < sizeof...(Ts), "index range: [0, size)");
return foo_impl<i>::get(vs...);// forward to "implementation"
}
使用C ++ 11,您需要指定尾随返回类型,这有点单调乏味。有关详细信息,请参阅max66's answer。
-Wall
是绝对最小值。)std::tuple
等标准解决方案。答案 1 :(得分:0)
我认为不可能(至少在C ++ 11和C ++ 14中)开发这种类型的foo()
,因为你不知道正确的退货类型。
如果您不想使用std::tuple
,我建议开发一种类型特征来提取第n种类型并通过SFINAE管理foo()
。
以下是可能的解决方案
#include <iostream>
#include <type_traits>
template <std::size_t, typename...>
struct indexType
{ using type = int; }; // the type of the foo() without argument
template <std::size_t I, typename I0, typename ... Is>
struct indexType<I, I0, Is...>
{ using type = typename indexType<I-1U, Is...>::type; };
template <typename I0, typename ... Is>
struct indexType<0U, I0, Is...>
{ using type = I0; };
template <std::size_t I, typename ... Args>
using indexType_t = typename indexType<I, Args...>::type;
template <std::size_t>
int foo ()
{ return 0; } // termination version: a return type is needed
template <std::size_t I, typename Arg, typename... Args>
auto foo (Arg const & head, Args const & ...)
-> typename std::enable_if<I == 0U, Arg>::type
{ return head; }
template <std::size_t I, typename Arg, typename... Args>
auto foo (Arg const &, Args const & ... args)
-> typename std::enable_if<I != 0U, indexType_t<I-1U, Args...>>::type
{ return foo<I-1U>(args...); }
int main ()
{
std::cout << foo<1U> (1, 3.1415, std::string("Test!")) << std::endl;
std::cout << foo<2U> (1, 3.1415, std::string("Test!")) << std::endl;
std::cout << foo<3U> (1, 3.1415, std::string("Test!")) << std::endl;
}