为什么在模板函数中将两个 xtensor 表达式加在一起会错误地广播?
Why does adding two xtensor expressions together in template function broadcast incorrectly?
考虑以下程序:
#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xview.hpp"
xt::xarray<double> arr1
{1.0, 2.0, 3.0};
xt::xarray<double> arr2
{5.0, 6.0, 7.0};
template <typename T, typename U>
struct container{
container(const T& t, const U& u) : a(t), b(u) {}
T a;
U b;
};
template <typename T, typename U>
container<T, U> make_container(const T& t, const U& u){
return container<T,U>(t, u);
}
auto c = make_container(arr1, arr1);
std::cout << (arr1 * arr1) + arr2;
template <typename A, typename B, typename R>
auto operator+(const container<A, B>& e1, const R& e2){
return (e1.a * e1.b) + e2;
}
std::cout << (c + arr2);
如果我们看一下代码:
std::cout << (arr1 * arr1) + arr2;
它将输出:
{ 6., 10., 16.}
然而,运行最后一行:
std::cout << (c + arr2);
产生以下结果:
{{ 6., 9., 14.}, { 7., 10., 15.}, { 8., 11., 16.}}
为什么会这样?我将 operator+
的函数定义更改为以下内容:
template <typename A, typename B, typename R>
auto operator+(const container<A, B>& e1, const R& e2){
std::cout << __PRETTY_FUNCTION__ << std::endl;
return (e1.b * e1.alpha) + e2;
}
输出有点令人惊讶:
auto operator+(const container<A, B> &, const R &) [A = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, B = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, R = double]
auto operator+(const container<A, B> &, const R &) [A = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, B = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, R = double]
auto operator+(const container<A, B> &, const R &) [A = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, B = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, R = double]
{{ 6., 9., 14.}, { 7., 10., 15.}, { 8., 11., 16.}}
为什么一个操作调用了3个+
操作?是否在某处定义了导致此行为的宏? operator+
中的 R
类型给我们 double
,实际上应该是 xt::xarray<double>
.
如有任何见解,我们将不胜感激。
在命名空间 xt
中定义的 operator+
采用通用引用,因此在您编写 c + arr2
.
时优先于重载
因此最后一行将 return 一个 xfunction
其第一个操作数是你的 container
,第二个是 xarray
.
现在,由于 container
不是 xexpression
,在 xfunction
中它被处理为... xscalar<container>
!
因此,当您尝试访问此 xfunction
的第 i 个元素时,将执行以下操作:xscalar<container> + arr2[i]
(广播 xscalar
)。由于 xscalar<container>
可转换为 container
,因此调用 operator+
重载时 R
解析为 arr2
的 value_type
,即 double
.
以下循环说明了此行为:
auto f = c + arr2;
for(auto iter = f.begin(); iter != f.end(); ++iter)
{
std::cout << *iter << std::endl;
}
它生成您的 operator+
的以下调用:
operator+(c, arr[0]);
operator+(c, arr[1]);
operator+(c, arr[2]);
这就是为什么您看到 operator+
.
的 3 个调用
考虑以下程序:
#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xview.hpp"
xt::xarray<double> arr1
{1.0, 2.0, 3.0};
xt::xarray<double> arr2
{5.0, 6.0, 7.0};
template <typename T, typename U>
struct container{
container(const T& t, const U& u) : a(t), b(u) {}
T a;
U b;
};
template <typename T, typename U>
container<T, U> make_container(const T& t, const U& u){
return container<T,U>(t, u);
}
auto c = make_container(arr1, arr1);
std::cout << (arr1 * arr1) + arr2;
template <typename A, typename B, typename R>
auto operator+(const container<A, B>& e1, const R& e2){
return (e1.a * e1.b) + e2;
}
std::cout << (c + arr2);
如果我们看一下代码:
std::cout << (arr1 * arr1) + arr2;
它将输出:
{ 6., 10., 16.}
然而,运行最后一行:
std::cout << (c + arr2);
产生以下结果:
{{ 6., 9., 14.}, { 7., 10., 15.}, { 8., 11., 16.}}
为什么会这样?我将 operator+
的函数定义更改为以下内容:
template <typename A, typename B, typename R>
auto operator+(const container<A, B>& e1, const R& e2){
std::cout << __PRETTY_FUNCTION__ << std::endl;
return (e1.b * e1.alpha) + e2;
}
输出有点令人惊讶:
auto operator+(const container<A, B> &, const R &) [A = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, B = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, R = double]
auto operator+(const container<A, B> &, const R &) [A = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, B = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, R = double]
auto operator+(const container<A, B> &, const R &) [A = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, B = xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<unsigned long, 4, std::allocator<unsigned long>, true>, xt::xtensor_expression_tag>, R = double]
{{ 6., 9., 14.}, { 7., 10., 15.}, { 8., 11., 16.}}
为什么一个操作调用了3个+
操作?是否在某处定义了导致此行为的宏? operator+
中的 R
类型给我们 double
,实际上应该是 xt::xarray<double>
.
如有任何见解,我们将不胜感激。
在命名空间 xt
中定义的 operator+
采用通用引用,因此在您编写 c + arr2
.
因此最后一行将 return 一个 xfunction
其第一个操作数是你的 container
,第二个是 xarray
.
现在,由于 container
不是 xexpression
,在 xfunction
中它被处理为... xscalar<container>
!
因此,当您尝试访问此 xfunction
的第 i 个元素时,将执行以下操作:xscalar<container> + arr2[i]
(广播 xscalar
)。由于 xscalar<container>
可转换为 container
,因此调用 operator+
重载时 R
解析为 arr2
的 value_type
,即 double
.
以下循环说明了此行为:
auto f = c + arr2;
for(auto iter = f.begin(); iter != f.end(); ++iter)
{
std::cout << *iter << std::endl;
}
它生成您的 operator+
的以下调用:
operator+(c, arr[0]);
operator+(c, arr[1]);
operator+(c, arr[2]);
这就是为什么您看到 operator+
.