web-dev-qa-db-ja.com

abs(double)のあいまいなオーバーロード呼び出し

次のC++コードがあります。

#include <math.h>
#include <cmath.h>      // per http://www.cplusplus.com/reference/clibrary/cmath/abs/

// snip ...

if ( (loan_balance < 0) && (abs(loan_balance) > loan_payment) ) {
    ...
}

makeが爆発します:

error: call of overloaded 'abs(double)' is ambiguous

また興味深い:

/usr/include/stdlib.h:785: note: candidates are: int abs(int)

浮動小数点を処理できるcmath.hのabs()をコンパイラーが呼び出す必要があることを指定するにはどうすればよいですか?

コンパイラー情報(これが重要かどうかわかりません):

[some_man@some_box ~/some_code]#  gcc -v
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr    /share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,Java,fortran,ada --enable-Java-awt=gtk --disable-dssi --enable-plugin --with-Java-home=/usr/lib/jvm/Java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --Host=i386-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-44)
56
some_man

ヘッダー_<math.h>_は、C std libヘッダーです。グローバル名前空間で多くのものを定義します。ヘッダー_<cmath>_は、そのヘッダーのC++バージョンです。名前空間stdで本質的に同じものを定義します。 (C++バージョンにはいくつかの関数のオーバーロードが付属しているなど、いくつかの違いがありますが、それは重要ではありません。)ヘッダー_<cmath.h>_は存在しません。

ベンダーは、本質的に同じヘッダーの2つのバージョンを維持することを望んでいないため、それらのうちの1つだけを背後で持つ異なる可能性を考え出しました。多くの場合、それはCヘッダーです(C++コンパイラーはそれを解析できますが、反対は機能しません)ので、C++ヘッダーにはそれが含まれ、名前空間stdにすべてを取り込みます。または、_namespace std_の有無にかかわらず、同じヘッダーを解析するためのマクロマジックがあります。これに加えて、一部の環境では、ヘッダーにファイル拡張子がない場合は扱いにくい(エディターがコードの強調表示に失敗するなど)。そのため、一部のベンダーは_<cmath>_を、_.h_拡張子を持つ他のヘッダーを含むワンライナーにします。または、一致するすべての_<cblah>_を_<blah.h>_にマップするものもあります(マクロマジックにより、___cplusplus_が定義されるとC++ヘッダーになり、そうでなければCヘッダーになります)または_<cblah.h>_または何でも。

_<cmath.h>_のようなものを含むいくつかのプラットフォームでは、存在しないはずの最初は成功しますが、後でコンパイラーが劇的に失敗する可能性があります。

どのstd lib実装を使用するのかわかりません。 GCCに付属していると思いますが、これはわかりません。そのため、あなたのケースで何が起こったのかを正確に説明することはできません。しかし、それは確かに上記のベンダー固有のハッキングの1つと、自分自身を含めるべきではないヘッダーを含めたものの混合物です。たぶん、それは_<cmath>_が_<cmath.h>_にマップし、特定の(セットの)マクロを定義していなかったため、両方の定義になったのかもしれません。

ただし、このコードはまだコンパイルすべきではないことに注意してください。

_#include <cmath>

double f(double d)
{
  return abs(d);
}
_

グローバル名前空間にabs()があってはなりません(std::abs()です)。ただし、上記の実装のトリックによると、可能性があります。そのようなコードを後で移植する(または、これを許可しないベンダーの次のバージョンでコンパイルしようとする)ことはvery退屈なので、これに注意する必要があります。

47
sbi

要約すると、math.hCからのもので、10年以上前に作成されました。 math.hでは、その原始的な性質により、abs()関数は整数型に対してのみ「本質的に」あり、doubleの絶対値を取得したい場合は、fabs()を使用する必要がありました。 。 C++が作成されたとき、math.hを取り、cmathにしました。 cmathは基本的にmath.hですが、C++向けに改善されています。 fabs()とabsを区別するなどの改善が行われ、double型と整数型の両方に対してabs()が作成されました。要約すると、math.hを使用して整数にabs()を使用し、doubleにfabs()を使用するか、cmathを使用してすべてにabsを使用します(簡単で推奨)。

これが同じ問題を抱えている人を助けることを願っています!

38

Abs()の代わりにfabs()を使用します。これは整数ではなくfloatの場合と同じです。

17
Nianliang