I want to provide some sqrt wrapper functions for simd types, such that they can be used from templates together with std::sqrt.
Now I have the problem, that they somehow are not visible. Only the ones defined in std can be used.
This a highly reduced part of the code:
#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;
}
Now clang gives this:
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); }
^
It says, that the simd sqrt functions "should be declared prior to the call site", but I think they are.
I did a web search, but unfortunately I only found cases, where the order of call and declaration actually was wrong.
I just commented out the using std::sqrt
and everything works fine. I don't get it... How it is able to find the std::sqrt ones now?
I use clang 3.9.1 from homebrew on macOS.
The using std::sqrt;
adds a declaration of sqrt
in the function body, so name lookup for the function finds that declaration and doesn't consider the ones outside the function body in the enclosing scope.
This is a form of "name hiding", which is a property of C++ where names in one scope "hide" entities with the same name in outer scopes. This happens because the compiler starts at the innermost scope and looks for a name, then only if there are no matches tries the enclosing scope, and so one until it reaches the outermost (i.e. global) scope. So it stops searching for the name once it finds one or matches in a given scope, and matching names in outer scopes don't get seen.
In your code the name sqrt
is declared in the function body by the using declaration that refers to the std::sqrt
function. Name lookup starts in the scope of that function body, finds a match, and doesn't look in the surrounding namespace.
You need to do:
using std::sqrt;
using ::sqrt;
That means that both sets of overloads (the ones you defined in the global namespace, and the ones the <cmath>
header defined in namespace std
) are declared in the function scope, and both can be found by name lookup. Now the compiler will find all those overloads in the function scope, and overload resolution will choose the best one based on the argument type.
Another option is to move the using std::sqrt
using-declaration to the global namespace, instead of in the function body. That will add std::sqrt
to the global namespace, so it doesn't hide your own sqrt
overloads. Without the using-declaration in the function body there will be no matches in the innermost scope, so the compiler will look in the enclosing scope, which is the global namespace. In that scope it finds all the overloads and and overload resolution will choose the best one.
Another option is to replace <cmath>
with <math.h>
and remove the using std::sqrt;
. That will ensure that the Standard Library version of sqrt
is declared in the global namespace instead of namespace std
, and therefore you don't need the using std::sqrt;
to be able to call it unqualified.
As you noted in the comment below, it might also work if you just comment out the using-declaration, however that is not portable and is not guaranteed to work. It happens to work with the compiler you used, because <cmath>
declares both std::sqrt
and ::sqrt
(so it's equivalent to having the using std::sqrt;
in the global namespace), but not all compilers do that. To guarantee that you get ::sqrt
in the global namespace you should put using std::sqrt;
in the global namespace or include <math.h>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With