Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
带空参数包的递归变量模板(避免基本情况下的重复) 我正在试用C++递归模板,我不知道为什么模板不起作用。_C++_C++11_Templates_Variadic Templates_Variadic Functions - Fatal编程技术网

带空参数包的递归变量模板(避免基本情况下的重复) 我正在试用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...);
}