tho*_*ink 3 c++ lookup clang c++11
我想为simd类型提供一些sqrt包装器函数,这样它们可以与std :: sqrt一起从模板中使用.
现在我遇到了问题,他们以某种方式不可见.只能使用std中定义的那些.
这是代码的一个高度减少的部分:
#include <cmath>
#include <pmmintrin.h>
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 <typename T>
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;
}
Run Code Online (Sandbox Code Playgroud)
现在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); }
^
Run Code Online (Sandbox Code Playgroud)
它说,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;
Run Code Online (Sandbox Code Playgroud)
这意味着两个重载集(您在全局命名空间中定义的重载,以及<cmath>在命名空间中定义的标头std)都在函数范围内声明,并且两者都可以通过名称查找找到.现在编译器将在函数范围中找到所有这些重载,并且重载决策将根据参数类型选择最佳的重载.
另一种选择是将using std::sqrtusing声明移动到全局命名空间,而不是在函数体中.这将添加std::sqrt到全局命名空间,因此它不会隐藏您自己的sqrt重载.如果函数体中没有using声明,则最内层范围内不会有匹配,因此编译器将查看封闭范围,即全局命名空间.在该范围内,它会找到所有重载,并且重载决策将选择最佳的重载.
另一种选择是,以取代<cmath>与<math.h>和删除using std::sqrt;.这将确保标准库版本sqrt在全局命名空间而不是命名空间中声明std,因此您不需要using std::sqrt;能够将其称为不合格.
正如您在下面的评论中所指出的,如果您只是注释掉using声明,它也可能有效,但是这不是可移植的,并且不能保证工作.它发生与您使用的编译工作,因为<cmath>声明都std::sqrt 和 ::sqrt(所以它等同于具有using std::sqrt;全局命名空间),但不是所有的编译器做.为了保证你进入::sqrt全局命名空间,你应该放入using std::sqrt;全局命名空间或include <math.h>.