2017-01-12 4 views
3

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

今打ち鳴らすこれを与える:

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で自作のclang 3.9.1を使用します。

答えて

9

using std::sqrt;は、関数本体にsqrtという宣言を追加するため、関数の名前検索によってその宣言が検出され、囲みスコープ内の関数本体外にあるものは考慮されません。

これは、名前スコープ内の名前が同じ名前のエンティティを外部スコープ内で「隠す」C++のプロパティである「名前隠蔽」の形式です。これはコンパイラが最も内側のスコープから開始して名前を検索し、一致するものがない場合にのみ囲みスコープを試みるので、最も外側の(つまりグローバル)スコープに到達するまで発生します。そのため、指定されたスコープで一致または一致するものが見つかると、その名前の検索が停止し、外側のスコープ内の一致する名前は認識されません。

コードでは、sqrtという名前は、関数本体で、std::sqrt関数を参照するusing宣言によって宣言されています。名前参照は、その関数本体のスコープ内で開始し、一致するものを見つけ出し、周囲の名前空間を調べません。

あなたが実行する必要があります。

using std::sqrt; 
using ::sqrt; 

過負荷の両方のセット(あなたはグローバル名前空間で定義されたもの、とするものの名​​前空間stdで定義された<cmath>ヘッダー)は関数スコープ内で宣言されていることを意味します両方とも名前のルックアップによって見つけることができます。コンパイラは関数のスコープ内でこれらのすべてのオーバーロードを検出し、オーバーロードの解決は引数の型に基づいて最適なものを選択します。

もう1つの方法は、関数本体ではなく、using宣言をグローバル名前空間に移動することです。これにより、グローバル名前空間にstd::sqrtが追加されるため、独自のsqrtオーバーロードが隠されることはありません。関数本体でusing宣言がなければ、最も内側のスコープに一致するものはないので、コンパイラはグローバルな名前空間である囲みスコープを調べます。このスコープでは、すべてのオーバーロードが検出され、オーバーロードの解像度が最適なオーバーレイを選択します。

もう1つの方法は、<cmath><math.h>に置き換えてusing std::sqrt;を削除することです。標準ライブラリのバージョンsqrtは、名前空間stdの代わりにグローバル名前空間で宣言されているため、using std::sqrt;を修飾しないと呼び出す必要はありません。

以下のコメントに記載されているように、使用宣言をコメントアウトするだけで動作する可能性がありますが、移植性がなく、動作保証されていません。 <cmath>std::sqrt::sqrtの両方を宣言しているので(コンパイル時にグローバル名前空間にusing std::sqrt;を持つのと同じです)、使用しているコンパイラで動作します。グローバル名前空間に::sqrtを確実に含めるには、using std::sqrt;をグローバル名前空間に入れるか、<math.h>を含める必要があります。

+2

はい。教科書の名前が隠れている。時々あなたのクラスでBase :: fooを使って石膏を塗りつぶしたのと同じ理由(ish)。間違いなくC++の「初心者」には直感的ではありませんが、最終的にはそれに慣れています。 –

+0

大丈夫ですよ、私は理解しています...なぜstd :: sqrtを完全に使用してコメントするとき、それがなぜ機能するのか知っていますか? – thorink

+0

@thorinkこれは「あなたがする必要がある」ためです:答えの一部は必要ありません。名前参照は、Cが標準ライブラリでそのように定義したので、実際にグローバル名前空間にあるsqrtを検索します。 – iheanyi

関連する問題