如何对多维std::vector的所有元素求和?
How to sum all the elements of a multi-dimensional std::vector?
这个想法简单明了:
继续将 n
维向量分解为 n-1
维组成向量,直到您可以访问原始数据类型对象。然后全部添加。
问题是,如何推断return类型?
可以这样做,但它已经假定求和变量的数据类型(return-类型):
typedef int SumType;
template <class T>
T Sum (const T x)
{
return x;
}
template <class T>
SumType Sum (const std::vector<T>& v)
{
SumType sum = 0;
for (const auto& x: v)
sum += Sum(x);
return sum;
}
但是我不想像上面那样做。我觉得这违背了元编程的精神。
我们必须通过将向量分解成其组成向量来推断 return 类型,直到我们到达原始数据类型对象,然后选择 return 类型作为原始数据类型.
在 C++ 中可以吗? (我是元编程的菜鸟)
P.S.
<numeric>
中的 std::accumulate()
可能会有所帮助,但它通过从其第三个参数 __init
.
推断出 return 类型来绕过问题
我们可以做的是使用 T.C.'s 来获取底层类型。即定义为
template<class T> struct voider { typedef void type; };
template<class T, class = void>
struct data_type {
typedef T type;
};
template<class T>
struct data_type<T, typename voider<typename T::value_type>::type>
: data_type<typename T::value_type> {};
使用它我们可以将主要 Sum
修改为
template <class T, class Ret = typename data_type<std::vector<T>>::type>
Ret Sum (const std::vector<T>& v)
{
Ret sum = 0;
for (const auto& x: v)
sum += Sum(x);
return sum;
}
那么你可以使用像
这样的东西
int main()
{
std::cout << Sum(std::vector<std::vector<std::vector<int>>>{{{1},{2},{3}},{{4},{5},{6}}});
}
输出
21
这可以在没有任何模板元编程的情况下完成。您可以让编译器使用 auto
和 decltype
:
推断类型
template <class T>
T Sum(const T x) {
return x;
}
template <class T>
auto Sum(const std::vector<T> &v) {
decltype(Sum(v[0])) sum = 0;
for (const auto &x : v)
sum += Sum(x);
return sum;
}
Sum
的 return 类型自动从 sum
推导出来,sum
的类型是 Sum(v[0])
return 的任何类型。最终你会得到 Sum
的第一个版本,其中 returns T
并且编译器知道那个类型。
您几乎已经找到了自己的答案。注意这一行:
sum += Sum(x);
我们所追求的 sum
的类型必须与递归调用 Sum
的结果相兼容。根据您的要求,其中一种类型肯定是调用的结果类型。
不过,我们不必仅仅依靠模糊的感觉。元编程毕竟是编程。您可能没有意识到,但是您的问题是有根据的递归之一,这意味着归纳原理可以指导我们找到答案。
在基本情况下,我们有一个数字非向量 element_type element;
,这意味着我们的结果类型是……element_type
。你实际上已经完成了这一步,这是第一次重载:
template<typename T>
T Sum(T element);
在递归情况下我们有:
std::vector<element_type> vec;
归纳假设,即:
// given
element_type element;
// we know the following is well-formed and a numerical type
using recursive_result_type = decltype( Sum(element) );
由于向量元素的类型为 element_type
,归纳假设告诉我们,对它们调用 Sum
的结果具有我们想要的所有属性。 (我们 +=
直觉的理由植根于此。)我们有我们的答案:我们按原样使用 recursive_result_type
。
现在事实证明,不能只写第二个重载,例如像这样:
// doesn't behave as expected
template<typename Element>
auto Sum(std::vector<Element> const& vec) -> decltype( Sum(vec.front()) );
原因是当前声明的 Sum
重载在 return 类型的范围内 而不是 (即使它在定义体中).解决这个问题的一种方法是依靠 class 范围,它更适应:
// defining a functor type with operator() overloads
// would work just as well
struct SumImpl {
template<typename Element>
static T apply(Element element)
{ return element; }
template<typename Element>
static auto apply(std::vector<Element> const& vec)
-> decltype( apply(vec.front()) )
{
using result_type = decltype( apply(vec.front()) );
result_type sum = 0;
for(auto const& element: vec) {
sum += apply(element);
}
return sum;
}
};
template<typename Arg>
using sum_result_t = decltype( SumImpl::apply(std::declval<Arg const&>()) );
template<typename Arg>
sum_result_t<Arg> Sum(Arg const& arg)
{ return SumImpl::apply(arg); }
这个想法简单明了:
继续将 n
维向量分解为 n-1
维组成向量,直到您可以访问原始数据类型对象。然后全部添加。
问题是,如何推断return类型?
可以这样做,但它已经假定求和变量的数据类型(return-类型):
typedef int SumType;
template <class T>
T Sum (const T x)
{
return x;
}
template <class T>
SumType Sum (const std::vector<T>& v)
{
SumType sum = 0;
for (const auto& x: v)
sum += Sum(x);
return sum;
}
但是我不想像上面那样做。我觉得这违背了元编程的精神。
我们必须通过将向量分解成其组成向量来推断 return 类型,直到我们到达原始数据类型对象,然后选择 return 类型作为原始数据类型.
在 C++ 中可以吗? (我是元编程的菜鸟)
P.S.
<numeric>
中的 std::accumulate()
可能会有所帮助,但它通过从其第三个参数 __init
.
我们可以做的是使用 T.C.'s
template<class T> struct voider { typedef void type; }; template<class T, class = void> struct data_type { typedef T type; }; template<class T> struct data_type<T, typename voider<typename T::value_type>::type> : data_type<typename T::value_type> {};
使用它我们可以将主要 Sum
修改为
template <class T, class Ret = typename data_type<std::vector<T>>::type>
Ret Sum (const std::vector<T>& v)
{
Ret sum = 0;
for (const auto& x: v)
sum += Sum(x);
return sum;
}
那么你可以使用像
这样的东西int main()
{
std::cout << Sum(std::vector<std::vector<std::vector<int>>>{{{1},{2},{3}},{{4},{5},{6}}});
}
输出
21
这可以在没有任何模板元编程的情况下完成。您可以让编译器使用 auto
和 decltype
:
template <class T>
T Sum(const T x) {
return x;
}
template <class T>
auto Sum(const std::vector<T> &v) {
decltype(Sum(v[0])) sum = 0;
for (const auto &x : v)
sum += Sum(x);
return sum;
}
Sum
的 return 类型自动从 sum
推导出来,sum
的类型是 Sum(v[0])
return 的任何类型。最终你会得到 Sum
的第一个版本,其中 returns T
并且编译器知道那个类型。
您几乎已经找到了自己的答案。注意这一行:
sum += Sum(x);
我们所追求的 sum
的类型必须与递归调用 Sum
的结果相兼容。根据您的要求,其中一种类型肯定是调用的结果类型。
不过,我们不必仅仅依靠模糊的感觉。元编程毕竟是编程。您可能没有意识到,但是您的问题是有根据的递归之一,这意味着归纳原理可以指导我们找到答案。
在基本情况下,我们有一个数字非向量
element_type element;
,这意味着我们的结果类型是……element_type
。你实际上已经完成了这一步,这是第一次重载:template<typename T> T Sum(T element);
在递归情况下我们有:
std::vector<element_type> vec;
归纳假设,即:
// given element_type element; // we know the following is well-formed and a numerical type using recursive_result_type = decltype( Sum(element) );
由于向量元素的类型为
element_type
,归纳假设告诉我们,对它们调用Sum
的结果具有我们想要的所有属性。 (我们+=
直觉的理由植根于此。)我们有我们的答案:我们按原样使用recursive_result_type
。
现在事实证明,不能只写第二个重载,例如像这样:
// doesn't behave as expected
template<typename Element>
auto Sum(std::vector<Element> const& vec) -> decltype( Sum(vec.front()) );
原因是当前声明的 Sum
重载在 return 类型的范围内 而不是 (即使它在定义体中).解决这个问题的一种方法是依靠 class 范围,它更适应:
// defining a functor type with operator() overloads
// would work just as well
struct SumImpl {
template<typename Element>
static T apply(Element element)
{ return element; }
template<typename Element>
static auto apply(std::vector<Element> const& vec)
-> decltype( apply(vec.front()) )
{
using result_type = decltype( apply(vec.front()) );
result_type sum = 0;
for(auto const& element: vec) {
sum += apply(element);
}
return sum;
}
};
template<typename Arg>
using sum_result_t = decltype( SumImpl::apply(std::declval<Arg const&>()) );
template<typename Arg>
sum_result_t<Arg> Sum(Arg const& arg)
{ return SumImpl::apply(arg); }