【问题标题】:Why is the sum 0?为什么总和是0?
【发布时间】:2013-05-13 11:20:03
【问题描述】:

我正在尝试创建一个通用的求和函数模板。此模板为left associative。下面是我的实现

    int result=0;
template <typename D, typename T>
const T &sum_helper(const D &d, const T &v) {
    result=result+v;
    return result;
}
int pass(...){}

template <typename D, typename T1, typename... Ts>
auto sum_helper(const D &d, const T1 &v1, const Ts &... params) -> decltype(v1 + sum_helper(d, params...)) {
    return v1 + sum_helper(d, params... );
}

class A {};

template <typename... Ns> 
struct seq {};

template <typename... Ts>
auto sum(const Ts &... params) -> decltype(sum_helper(A(), params...)) 
{
    return  pass((sum_helper(seq<Ts...>(),params)) ...,1);

}

但是当我像sum(1,2,3,4) 这样称呼它时,它总是输出0。怎么了?我知道应该更正pass。但是有什么办法可以改正呢?

【问题讨论】:

  • pass/seq 有什么用?

标签: c++ templates c++11 g++ variadic-templates


【解决方案1】:

原始答案不起作用,因为尾随返回类型在其声明点之前使用了重载。在知道函数的返回类型之前,也不可能转发声明函数。所以我们需要一个辅助结构。这是工作的(不幸的是现在非常复杂)版本:

#include <utility>

template <typename...>
struct sum_impl;

/* This is the base case */
template <typename T1, typename T2>
struct sum_impl<T1, T2>
{
    typedef decltype(std::declval<const T1&>() + std::declval<const T2&>()) result_type;

    static result_type doit(const T1& v1, const T2& v2)
    {
        return v1 + v2;
    }
};

/* And here is the recursive definition for left-associativity */
template <typename T1, typename T2, typename... Ts>
struct sum_impl<T1, T2, Ts...>
{
    typedef decltype(std::declval<const T1&>() + std::declval<const T2&>()) step_type;
    typedef typename sum_impl<step_type, Ts...>::result_type result_type;

    static result_type doit(const T1& v1, const T2& v2, const Ts&... rest)
    {
        return sum_impl<step_type, Ts...>::doit(v1 + v2, rest...);
    }
};

template <typename... Ts>
typename sum_impl<Ts...>::result_type sum(const Ts&... args)
{
    return sum_impl<Ts...>::doit(args...);
}

演示:http://ideone.com/jMwgLz


这是一个保留 Named 答案的简单性但保留关联性的版本:

/* not really needed, unless someone wants to call sum with only a single argument */
template <typename T>
T sum(const T& v)
{
    return v;
}

/* This is the base case */
template <typename T1, typename T2>
auto sum(const T1& v1, const T2& v2) -> decltype( v1 + v2 )
{
    return v1 + v2;
}

/* And here is the recursive definition for left-associativity */
template <typename T1, typename T2, typename... Ts>
auto sum(const T1& v1, const T2& v2, const Ts&... rest) -> decltype( sum(v1 + v2, rest...) )
{
    return sum(v1 + v2, rest... );
}

【讨论】:

  • 是的,这看起来比我的好:P
  • @Ben 这太棒了!几乎正确.. 但如果我给出超过 4 个参数,它似乎无法编译
  • @footy: I see,这是因为尾随返回类型使用递归,但它在当前重载的声明点之前。其他答案不会同样失败吗?
  • 而且,看起来有人已经想通了:stackoverflow.com/questions/6065810/…
【解决方案2】:

这是一个更简单的解决方案:

#include <iostream>

using namespace std;

template <typename T1>
auto _sum(T1 & _ret, const T1 & _t1) -> T1
{
    _ret += _t1;
    return _ret;
}

template <typename T1, typename... Ts>
auto _sum(T1 & _ret, const T1 & _t1, const Ts &... params) -> T1 
{
    _ret += _t1;
    return _sum(_ret, params...);
}

template <typename T1, typename... Ts>
auto sum(const T1 & _t1, const Ts &... params) -> T1 
{
    T1 ret = _t1;
    return _sum(ret, params...);    
}

int main()
{
    cout << sum(1, 2, 3, 4, 5) << endl;
    return 0;
}

【讨论】:

  • 谢谢!你能解释一下这个左联想是怎么回事吗?
  • @footy:因为它计算 1 + (2 + 3 + 4 + 5)。请注意,左关联通常不会固定评估顺序。
  • 我在重载 + 运算符后尝试了以下对象。它说template argument deduction/substitution failed .. 怎么样?我无法理解
  • 1+(...) 是右结合的。左联想是 (...)+5。
  • @Rollie 我需要左关联
【解决方案3】:

但是当我像 sum(1,2,3,4) 一样调用它时,它总是输出 0。怎么了?

那是因为pass 没有返回任何东西,所以你这里有 未定义的行为,因为你正在流出一个非 void 函数而没有返回任何东西。

我不知道你为什么在这里需要pass

return  pass((sum_helper(seq<Ts...>(),params)) ...,1);

您可以扩展可变参数并将它们直接发送到sum_helper。像这样

return  sum_helper(seq<Ts...>(),params...);


但是更简单的版本会是

template <typename T>
T sum(const T& v) {
    return v;
}

template <typename T1, typename T2>
auto sum(const T1& v1, const T2& v2) -> decltype( v1 + v2) {
    return v1 + v2;
}

template <typename T1, typename T2, typename... Ts>
auto sum(const T1& v1, const T2& v2, const Ts&... rest) -> decltype( v1 + v2 + sum(rest...) ) {
    return v1 + v2 + sum(rest... );
}

int main() {
    cout << sum(1,2,3,4);    
}

Rollie 的回答提供了一个更简单的版本。

【讨论】:

  • 我在重载 + 运算符后尝试了以下对象。它说模板参数推导/替换失败..如何?我无法理解
  • 我认为您的解决方案不适用于sum(1,2,3);或任何奇数个参数;它将去可变参数和调用1+2+sum(3),而sum(3)没有定义。
  • @Rollie 是的。我已经修好了。我在 sum 的长版本中选择两个 args 的原因是因为 gcc 由于某种原因无法正确扣除 decltype( v1 + sum(rest...) )。当有两个 args 匹配并且其余的扩展时,它匹配它。
  • @footy 什么样的错误信息?你要添加什么样的对象?
猜你喜欢
  • 2020-07-07
  • 2021-04-14
  • 2011-12-28
  • 2011-12-28
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 1970-01-01
  • 2016-09-16
相关资源
最近更新 更多