2017-01-12 6 views
3

Я хочу предоставить некоторые функции оболочки sqrt для simd-типов, чтобы их можно было использовать из шаблонов вместе со std :: sqrt.Почему clang не находит функцию, объявленную до места вызова?

Теперь у меня есть проблема, что они как-то не видны. Могут использоваться только те, которые определены в 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; 
} 

Теперь лязг дает это:

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); } 
     ^

Он говорит, что функции SQRT SIMD «должны быть объявлены до сайта вызова» , но я думаю, что они есть.

Я сделал веб-поиск, но, к сожалению, я нашел случаи, когда порядок вызова и декларация на самом деле были неправильными.

Я только что прокомментировал using std::sqrt и все работает нормально. Я не понимаю ... Как он теперь может найти std :: sqrt?

Я использую clang 3.9.1 из homebrew на macOS.

ответ

9

using std::sqrt; добавляет в тело функции объявление sqrt, поэтому поиск имени для функции находит это объявление и не рассматривает те, которые находятся вне тела функции в охватывающей области.

Это форма «скрытия имени», которая является свойством C++, где имена в одной области «скрывают» сущности с тем же именем во внешних областях. Это происходит из-за того, что компилятор запускается в самой внутренней области и ищет имя, тогда только если нет совпадений, он пытается охватить область охвата и, следовательно, до тех пор, пока не достигнет самой внешней (то есть глобальной) области. Таким образом, он прекращает поиск имени после его поиска или сопоставления в заданной области, и совпадение имен во внешних областях не видно.

В вашем коде имя sqrt объявлено в теле функции с помощью объявления, которое ссылается на функцию std::sqrt. Поиск имени начинается с области действия этого тела функции, находит совпадение и не смотрит в окружающее пространство имен.

Вам нужно сделать:

using std::sqrt; 
using ::sqrt; 

Это означает, что оба набор перегрузок (те, которые вы указали в глобальном пространстве имен, а также те, <cmath> заголовка определен в пространстве имен std) объявлены в области видимости функции , и оба могут быть найдены по имени. Теперь компилятор найдет все эти перегрузки в области функций, и разрешение перегрузки выберет лучший из них в зависимости от типа аргумента.

Другим вариантом является перемещение using std::sqrt с использованием декларации в глобальное пространство имен, а не в тело функции. Это добавит std::sqrt в глобальное пространство имен, поэтому оно не скроет ваши собственные sqrt перегрузки. Без использования-объявления в теле функции не будет совпадений в самой внутренней области, поэтому компилятор будет искать в охватывающей области, которая является глобальным пространством имен. В этой области он находит все перегрузки и разрешение перегрузки выбирает лучший.

Другой вариант заключается в замене <cmath> на <math.h> и удалении using std::sqrt;.Это гарантирует, что версия стандартной библиотеки sqrt будет объявлена ​​в глобальном пространстве имен вместо пространства имен std, и поэтому вам не понадобится using std::sqrt;, чтобы иметь возможность назвать его неквалифицированным.

Как вы отметили в приведенном ниже комментарии, оно также может работать, если вы просто закомментируете использование-декларации, однако это не переносится и не гарантируется работа. Случается, что вы работаете с компилятором, потому что <cmath> объявляет и::sqrt (так что это эквивалентно наличию using std::sqrt; в глобальном пространстве имен), но не все компиляторы делают это. Чтобы гарантировать, что вы получите ::sqrt в глобальном пространстве имен, вы должны поместить using std::sqrt; в глобальное пространство имен или включить <math.h>.

+2

Да. Имя учебника скрывается. По той же причине (ish), что вы иногда должны использовать гипс 'using Base :: foo' в своих классах. Определенно не интуитивно понятный для начинающего C++, но вы в конце концов привыкаете к нему. –

+0

хорошо, правильно, я понимаю ... Вы также знаете, почему это работает, когда комментируете 'using std :: sqrt' целиком? – thorink

+0

@thorink, потому что «вам нужно сделать:» часть ответа не требуется. Поиск имени будет искать sqrt, который фактически находится в глобальном пространстве имен, так как C определил его таким образом в своей стандартной библиотеке. – iheanyi