我想为simd类型提供一些sqrt包装器函数,这样它们可以与std :: sqrt一起从模板中使用.
现在我遇到了问题,他们以某种方式不可见.只能使用std中定义的那些.
这是代码的一个高度减少的部分:
#include#include using float_simd = __m128; using double_simd = __m128d; float_simd sqrt(float_simd a) { return _mm_sqrt_ps(a); } double_simd sqrt(double_simd a) { return _mm_sqrt_pd(a); } template void do_fancy(T val) { using std::sqrt; auto ret = sqrt(val); (void)ret; } int main() { double testDouble = 1.0; float testFloat = 1.0f; double_simd testSimdDouble; float_simd testSimdFloat; do_fancy(testDouble); do_fancy(testFloat); do_fancy(testSimdDouble); do_fancy(testSimdFloat); return 0; }
现在clang给出了这个:
main.cpp:14:16: error: call to function 'sqrt' that is neither visible in the template definition nor found by argument-dependent lookup auto ret = sqrt(val); ^ main.cpp:25:5: note: in instantiation of function template specialization 'do_fancy<__attribute__((__vector_size__(2 * sizeof(double)))) double>' requested here do_fancy(testSimdDouble); ^ main.cpp:8:13: note: 'sqrt' should be declared prior to the call site double_simd sqrt(double_simd a) { return _mm_sqrt_pd(a); } ^ main.cpp:14:16: error: call to function 'sqrt' that is neither visible in the template definition nor found by argument-dependent lookup auto ret = sqrt(val); ^ main.cpp:26:5: note: in instantiation of function template specialization 'do_fancy<__attribute__((__vector_size__(4 * sizeof(float)))) float>' requested here do_fancy(testSimdFloat); ^ main.cpp:7:12: note: 'sqrt' should be declared prior to the call site float_simd sqrt(float_simd a) { return _mm_sqrt_ps(a); } ^
它说,simd sqrt函数"应该在调用站点之前声明",但我认为它们是.
我做了网络搜索,但不幸的是我只发现了一些案例,其中呼叫和声明的顺序实际上是错误的.
我刚评论出来using std::sqrt
,一切正常.我不明白......现在怎样才能找到std :: sqrt?
我在macOS上使用homebrew的clang 3.9.1.
该using std::sqrt;
增加的声明sqrt
在函数体,所以对于函数名称查找发现的声明,并没有考虑在封闭范围在函数体外部的人.
这是"名称隐藏"的一种形式,它是C++的一个属性,其中一个范围中的名称"隐藏"外部作用域中具有相同名称的实体.发生这种情况是因为编译器从最里面的范围开始并查找名称,然后只有在没有匹配的情况下才会尝试封闭范围,因此只有在到达最外层(即全局)范围之前.因此,一旦找到一个或在给定范围内匹配,它就会停止搜索名称,并且不会看到外部作用域中的匹配名称.
在您的代码中,名称sqrt
通过引用该std::sqrt
函数的using声明在函数体中声明.名称查找从该函数体的范围开始,查找匹配,并且不查看周围的命名空间.
你需要这样做:
using std::sqrt; using ::sqrt;
这意味着两个重载集(您在全局命名空间中定义的重载,以及
在命名空间中定义的标头std
)都在函数范围内声明,并且两者都可以通过名称查找找到.现在编译器将在函数范围中找到所有这些重载,并且重载决策将根据参数类型选择最佳的重载.
另一种选择是将using std::sqrt
using声明移动到全局命名空间,而不是在函数体中.这将添加std::sqrt
到全局命名空间,因此它不会隐藏您自己的sqrt
重载.如果函数体中没有using声明,则最内层范围内不会有匹配,因此编译器将查看封闭范围,即全局命名空间.在该范围内,它会找到所有重载,并且重载决策将选择最佳的重载.
另一种选择是,以取代
与
和删除using std::sqrt;
.这将确保标准库版本sqrt
在全局命名空间而不是命名空间中声明std
,因此您不需要using std::sqrt;
能够将其称为不合格.
正如您在下面的评论中所指出的,如果您只是注释掉using声明,它也可能有效,但是这不是可移植的,并且不能保证工作.它发生与您使用的编译工作,因为
声明都std::sqrt
和 ::sqrt
(所以它等同于具有using std::sqrt;
全局命名空间),但不是所有的编译器做.为了保证你进入::sqrt
全局命名空间,你应该放入using std::sqrt;
全局命名空间或include
.