带空参数包的递归变量模板(避免基本情况下的重复) 我正在试用C++递归模板,我不知道为什么模板不起作用。
假设我想定义一个递归函数,它接受可变数量的参数(针对不同类型) 我已经看过很多可变模板的例子,到目前为止,我所看到的都是使用单独的模板专门化来指定基本情况 但是,我认为最好(至少在某些情况下)使用一个模板,它定义了基本情况和递归情况 我认为,如果函数中有很多公共逻辑,那么这种方法特别好,因为必须为基本实例复制这些逻辑(在两个不同的地方使用完全相同的代码) 下面示例中的第二个模板应该是我的解决方案。我认为这个模板应该独立运行。但事实并非如此 如果没有第一个模板,代码将无法编译:带空参数包的递归变量模板(避免基本情况下的重复) 我正在试用C++递归模板,我不知道为什么模板不起作用。,c++,c++11,templates,variadic-templates,variadic-functions,C++,C++11,Templates,Variadic Templates,Variadic Functions,假设我想定义一个递归函数,它接受可变数量的参数(针对不同类型) 我已经看过很多可变模板的例子,到目前为止,我所看到的都是使用单独的模板专门化来指定基本情况 但是,我认为最好(至少在某些情况下)使用一个模板,它定义了基本情况和递归情况 我认为,如果函数中有很多公共逻辑,那么这种方法特别好,因为必须为基本实例复制这些逻辑(在两个不同的地方使用完全相同的代码) 下面示例中的第二个模板应该是我的解决方案。我认为这个模板应该独立运行。但事实并非如此 如果没有第一个模板,代码将无法编译: error: no
error: no matching function for call to
'add_elems'
return head[i] + add_elems(i, second, tail...);
^~~~~~~~~
in instantiation of function
template specialization 'add_elems<double, std::__1::vector<double, std::__1::allocator<double> >>' requested here
...
问题是您的
if
语句不是constexpr
。这意味着对add\u elems
这意味着您最终会遇到这样一种情况,即tail
只是一个元素,编译器需要计算add\u elems(size\t&,const,std::vector&)
,它不存在,因为没有第二个参数
如果您能够拥有一条语句,那么这一切都会很好地工作,因为编译器甚至不会在错误分支的计算结果为false时编译它,因此也不会查找不存在的函数:
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
if constexpr (sizeof...(tail) > 0)
return head[i] + add_elems(i, second, tail...);
else
return head[i] + second[i];
}
(需要Clang 3.6.0或更高版本和-std=c++1z
选项。)正如错误消息所说,您目前的问题是,在tail为空的情况下,调用add_elems(i,second,tail…
与函数的定义不匹配。即使if语句中的布尔表达式是constexpr,在c++1z之前,函数的整个主体都必须是有效的
@AndyG提供了一种c++1z处理此问题的方法,另一种方法是使用if constexpr
,它允许“编译时分支”。这两种方法中的任何一种都允许您对模板进行一个(主要)专业化
// Only in c++1z
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
/* Imagine some more code here (the same as above) */
if constexpr (sizeof...(tail) > 0)
return head[i] + add_elems(i, second, tail...); // this is not required to be valid when the if is false
else
return head[i] + second[i]; // this is not required to be valid when the if is true (but it is happens to be valid anyway)
}
//仅在c++1z中
模板
V添加元素(大小i,常数标准::向量和头部,常数S和第二,常数t和…尾部)
{
/*想象一下这里有更多的代码(同上)*/
如果constexpr(sizeof…(tail)>0)
return head[i]+add_elems(i,second,tail…)//如果if为false,则不要求有效
其他的
return head[i]+second[i];//当if为true时,这不要求是有效的(但它恰好是有效的)
}
在等待C++17的时候,我提出了一个C++11不太好的解决方案,紧随其后的是AndyG解决方案
template <typename T0, typename ... T>
auto add_elems2 (size_t i, T0 const & elem0, T const & ... elems)
-> decltype(elem0[i])
{
using unused=int[];
auto ret = elem0[i];
unused a { (ret += elems[i], 0)... };
return ret;
}
模板
自动添加元素2(大小i、T0常量和元素0、t常量和…元素)
->decltype(elem0[i])
{
使用unused=int[];
自动回复=elem0[i];
未使用的a{(ret+=elems[i],0)…};
返回ret;
}
正如许多人所指出的,这在C++1z中很容易实现。它可以在C++14中完成,只是很难
template<class True, class False>
True pick( std::true_type, True t, False ) {
return std::move(t);
}
template<class True, class False>
False pick( std::false_type, True, False f ) {
return std::move(f);
}
template<bool b>
constexpr std::integral_constant<bool, b> bool_k;
template<typename V, typename S, typename... T>
V add_elems(size_t i, const std::vector<V>& head, const S& second, const T&... tail)
{
return
pick( bool_k<(sizeof...(tail)>0)>,
[&](const auto&... tail)->V{
// tail... template argument hides function argument above:
return head[i] + add_elems(i, second, tail...);
},
[&]()->V{
return head[i] + second[i];
}
)
( tail... );
};
模板
真拾取(标准::真_类型、真t、假){
返回std::move(t);
}
模板
假拾取(标准::假_类型、真、假f){
返回std::move(f);
}
模板
constexpr std::积分常数bool_k;
模板
V添加元素(大小i,常数标准::向量和头部,常数S和第二,常数t和…尾部)
{
返回
拾取(bool_k0)>,
[&](常数自动和…尾部)->V{
//尾部…模板参数隐藏上面的函数参数:
返回头[i]+添加元素(i,第二,尾部…);
},
[&]()->V{
返回头[i]+秒[i];
}
)
(尾巴…);
};
我们使用pick
对两个lambda中的一个执行编译时分派
这些lambda接受的代码部分随auto
参数而变化,这使它们成为模板。只要它们对某组<代码>自动< /COD>参数(即使是“从不调用”)有效,它们是合法的C++。
我们已经准备好在lambdas中隐藏这两个重载。由于C++11没有模板lambda,这种“隐藏重载”技术在C++11中不起作用。您可以使用它来模拟C++14中的if constexpr
的行为。例如:
template <typename...>
struct is_empty_pack : hana::integral_constant<bool, false> {};
template <>
struct is_empty_pack<> : hana::integral_constant<bool, true> {};
template <typename T, typename... Ts>
auto sum(T const& t, Ts const&... ts) {
return hana::if_(is_empty_pack<Ts...>{},
[](auto const& t) { return t; },
[](auto const& t, auto const&... ts) { return t + sum(ts...); }
)(t, ts...);
}
模板
结构是空的:hana::整型常数{};
模板
结构是空的:hana::整型常数{};
模板
自动求和(T常量和T,Ts常量和…Ts){
返回hana::如果uU(是空的U pack{},
[](auto const&t){return t;},
[](自动常数&t,自动常数&ts){返回t+sum(ts..;}
)(t,ts…);
}
定义“不工作”和“中断”。如果(sizeof…(tail)>0)
是运行时评估,而不是编译时。您使用的是什么版本的Clang?@AndyG:您所说的“运行时评估”是什么意思sizeof
根本不计算它的参数,这只是sizeof…(t)
@AndyG-ah,但是在编译时,如果(sizeof…(tail)>0)返回head[i]+add元素(i,second,tail…)代码>仍将转换为:如果(sizeof…([])>0)返回头[i]+添加元素(i,秒)代码>例如,条件无限递归(条件始终为false)?我正在使用苹果LLVM 8.1.0版(clang-802.0.38)
btw。不,它不是那样工作的。是的,简单的扩展很好。不过,我从未觉得它可读性很强。见鬼,我也不觉得折叠表达式特别可读,尽管它们很有用。实际上,我可能主张保留基本模板/可变模板模式(稍加修改),因为这对他们/他们的团队来说可能更容易理解。@AndyG-我同意:这个解决方案一点也不优雅(不可读)。等待C++17
template <typename...>
struct is_empty_pack : hana::integral_constant<bool, false> {};
template <>
struct is_empty_pack<> : hana::integral_constant<bool, true> {};
template <typename T, typename... Ts>
auto sum(T const& t, Ts const&... ts) {
return hana::if_(is_empty_pack<Ts...>{},
[](auto const& t) { return t; },
[](auto const& t, auto const&... ts) { return t + sum(ts...); }
)(t, ts...);
}