当前位置:  开发笔记 > 编程语言 > 正文

为什么clang在调用站点之前找不到声明的函数?

如何解决《为什么clang在调用站点之前找不到声明的函数?》经验,为你挑选了1个好方法。

我想为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.



1> Jonathan Wak..:

using std::sqrt;增加的声明sqrt在函数体,所以对于函数名称查找发现的声明,并没有考虑在封闭范围在函数体外部的人.

这是"名称隐藏"的一种形式,它是C++的一个属性,其中一个范围中的名称"隐藏"外部作用域中具有相同名称的实体.发生这种情况是因为编译器从最里面的范围开始并查找名称,然后只有在没有匹配的情况下才会尝试封闭范围,因此只有在到达最外层(即全局)范围之前.因此,一旦找到一个或在给定范围内匹配,它就会停止搜索名称,并且不会看到外部作用域中的匹配名称.

在您的代码中,名称sqrt通过引用该std::sqrt函数的using声明在函数体中声明.名称查找从该函数体的范围开始,查找匹配,并且不查看周围的命名空间.

你需要这样做:

using std::sqrt;
using ::sqrt;

这意味着两个重载集(您在全局命名空间中定义的重载,以及在命名空间中定义的标头std)都在函数范围内声明,并且两者都可以通过名称查找找到.现在编译器将在函数范围中找到所有这些重载,并且重载决策将根据参数类型选择最佳的重载.

另一种选择是将using std::sqrtusing声明移动到全局命名空间,而不是在函数体中.这将添加std::sqrt到全局命名空间,因此它不会隐藏您自己的sqrt重载.如果函数体中没有using声明,则最内层范围内不会有匹配,因此编译器将查看封闭范围,即全局命名空间.在该范围内,它会找到所有重载,并且重载决策将选择最佳的重载.

另一种选择是,以取代和删除using std::sqrt;.这将确保标准库版本sqrt在全局命名空间而不是命名空间中声明std,因此您不需要using std::sqrt;能够将其称为不合格.

正如您在下面的评论中所指出的,如果您只是注释掉using声明,它也可能有效,但是这不是可移植的,并且不能保证工作.它发生与您使用的编译工作,因为声明都std::sqrt ::sqrt(所以它等同于具有using std::sqrt;全局命名空间),但不是所有的编译器做.为了保证你进入::sqrt全局命名空间,你应该放入using std::sqrt;全局命名空间或include .


是啊.教科书名称隐藏innit.同样的原因(ish)你有时会在你的课堂上使用Base :: foo`.绝对不是C++"初学者"的直觉,但你最终会习惯它.
推荐阅读
勤奋的瞌睡猪_715
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有