我想为uint32x4_t
中ARM系统上定义的arm_neon.h
重载bitshift运算符。
struct uint32x4_t {
uint32_t val[4];
};
这应该通过调用SIMD函数来完成,该函数期望值移位并立即保持恒定:
uint32x4_t simdShift(uint32x4_t, constant_immediate);
shift.h
#ifndef SHIFT_H
#define SHIFT_H
namespace A {
namespace B {
/*uint32x4_t simdLoad(uint32_t*) {
...
}*/
template<int N>
uint32x4_t shiftRight(uint32x4_t vec) {
return vshrq_n_u32(vec,N);
}
}
}
uint32x4_t operator>>(uint32x4_t const & vec, const int v) {
return A::B::shiftRight<v>(vec);
}
#endif
的main.cpp
#include "shift.h"
int main() {
uint32_t* data = new uint32_t[4];
data[0] = 1;
data[1] = 2;
data[2] = 3;
data[3] = 4;
uint32x4_t reg;// = simdLoad(data);
reg = reg>>3;
return 0;
}
此代码产生错误:
'uint32x4_t operator&gt;&gt;(const uint32x4_t&amp;,int)'必须有一个参数 类或枚举类型uint32x4_t运算符&gt;&gt;(uint32x4_t const&amp; vec,const int v){
是否有针对operator>>
等“原生”类型重载uint32x4_t
的解决方法?
编辑:我调整了建议的解决方法,但错误仍然保持不变:(
答案 0 :(得分:3)
对ErmIg的回答逐步改进:
template<int N>
constexpr std::integral_constant<int, N> i_{};
template<int N>
uint32x4_t operator >>(uint32x4_t value, std::integral_constant<int, N>) noexcept {
return _mm_slli_si128(value, N);
}
int main() {
std::uint32_t data[4] = {1, 2, 3, 4};
uint32x4_t reg;// = simdLoad(&data);
reg = reg >> i_<3>;
}
N.b。我已将operator>>
放在全局命名空间中;如果你想把它放在一个不同的命名空间中,你需要在使用它之前将操作符带到范围内。
答案 1 :(得分:2)
&#34; uint32x4_t是本机类型,由arm_neon.h提供。&#34; (来自另一条评论)。
您最初面临的问题是C ++使用了一种名为Argument-Dependent Lookup的东西。对于A::B::uint32x4
,C ++会考虑A::B::operator>>(uint32x4, int)
。也就是说,C ++将查看相应参数的名称空间。
您的问题是uint32x4
位于全局命名空间中,但您将operator>>
放在另一个命名空间中。这是错的。把它放在正确的命名空间中。
请注意,命名空间是为避免名称冲突而提供的两种机制之一。重载是另一种机制。命名空间适用于所有类型的名称:变量,类型和函数。重载仅适用于函数。但在这种情况下,这是足够的,因为运算符是函数的子集。你不会得到名字冲突;您的operator>>
重载与其他operator>>
重叠。
答案 2 :(得分:2)
对于使用常量立即调用函数(它经常在SIMD内在函数中遇到)我通常使用带有整数模板参数的模板函数。以下示例使用SSE2,但对于NEON,它将类似:
template<int shift> __m128i Shift(__m128i value)
{
return _mm_slli_si128(value, shift);
}
int main()
{
__m128i a = _mm_set1_epi8(3);
__m128i b = Shift<2>(a);
return 0;
}
不幸的是,我不知道它是如何为C ++运算符制作的。当然,我们可以使用模板参数创建一个运算符,但使用它非常不方便:
template<int shift> __m128i operator >> (__m128i value, int shift_)
{
return _mm_slli_si128(value, shift);
}
int main()
{
__m128i a = _mm_set1_epi8(3);
__m128i b = operator >> <2>(a, 2);
return 0;
}
受@ildjarn启发的变种:
template<int N> struct Imm {};
#define IMM(N) Imm<N>()
template<int shift> __m128i operator >> (__m128i value, Imm<shift>)
{
return _mm_slli_si128(value, shift);
}
int main()
{
__m128i a = _mm_set1_epi8(3);
__m128i b = a >> IMM(2);
return 0;
}
答案 3 :(得分:1)
一种解决方案是将operator>>
从A::B
命名空间移动到全局命名空间。如果所有其他符号都是不同的名称空间,那么您只需要对它们进行限定。例如,如果simdShift
位于A::B
,您仍然可以拥有这样的全局operator>>
:
uint32x4_t operator>>(uint32x4_t const & vec, const int v) {
return A::B::simdShift(vec, v);
}
但我想让operator>>
成为uint32x4_t
的成员更合适:
struct uint32x4_t {
uint32_t val[4];
uint32x4_t operator>>(const int v) const;
};
namespace A { namespace B {
/// TODO: Put declaration/definition of simdShift here
}} // namespace A { namespace B {
uint32x4_t uint32x4_t::operator>>(const int v) const {
return A::B::simdShift(*this, v);
}
或者,作为评论中建议的ildjarn,将A::B
命名空间中的符号拉入您使用它们的上下文中:
using namespace A::B;