web-dev-qa-db-ja.com

<cmath>の一部の関数がstd名前空間にないのはなぜですか?

私は複数の算術タイプで動作するプロジェクトを開発しています。そこで、ユーザー定義の算術型の最小要件が定義されているヘッダーを作成しました。

ser_defined_arithmetic.h:

typedef double ArithmeticF;   // The user chooses what type he 
                              // wants to use to represent a real number

namespace arithmetic          // and defines the functions related to that type
{

const ArithmeticF sin(const ArithmeticF& x);
const ArithmeticF cos(const ArithmeticF& x);
const ArithmeticF tan(const ArithmeticF& x);
...
}

私を悩ませているのは、次のようなコードを使用する場合です。

#include "user_defined_arithmetic.h"

void some_function()
{
    using namespace arithmetic;
    ArithmeticF lala(3);
    sin(lala);
}

コンパイラエラーが発生します:

error: call of overloaded 'sin(ArithmeticF&)' is ambiguous
candidates are:
double sin(double)
const ArithmeticF arithmetic::sin(const ArithmeticF&)

<math.h>ヘッダーを使用したことはなく、<cmath>のみを使用しました。ヘッダーファイルでusing namespace stdを使用したことはありません。

Gcc4.6。*を使用しています。あいまいな宣言を含むヘッダーを確認したところ、次のようになりました。

mathcalls.h:

Prototype declarations for math functions; helper file for <math.h>.
...

<cmath>には<math.h>が含まれていることは知っていますが、std名前空間による宣言を保護する必要があります。 <cmath>ヘッダーを調べて、次のことを見つけました。

cmath.h:

...

#include <math.h>

...

// Get rid of those macros defined in <math.h> in lieu of real functions.
#undef abs
#undef div
#undef acos
...

namespace std _GLIBCXX_VISIBILITY(default)
{
...

したがって、名前空間stdが始まりますafter#include <math.h>。ここに何か問題がありますか、それとも私は何かを誤解しましたか?

27
Martin Drozdik

C++標準ライブラリの実装では、グローバル名前空間とstdでCライブラリ関数を宣言できます。 (あなたが見つけたように)名前空間の汚染はあなた自身の名前との衝突を引き起こす可能性があるので、これを間違いと呼ぶ人もいます。しかし、そういう風に生きていかなければなりません。名前をarithmetic::sinとして修飾する必要があります。

標準(C++ 11 17.6.1.2/4)の言葉で:

ただし、C++標準ライブラリでは、宣言(Cでマクロとして定義されている名前を除く)は、名前空間stdの名前空間スコープ(3.3.6)内にあります。 これらの名前が最初にグローバル名前空間スコープ内で宣言されるかどうかは指定されていませんその後、明示的な使用によって名前空間stdに挿入されます-宣言(7.3.3)。

19
Mike Seymour

本当に必要な場合は、次の行に沿って、いつでもcmathの周りに小さなラッパーを書くことができます。

//stdmath.cpp
#include <cmath>
namespace stdmath
{
    double sin(double x)
    {
        return std::sin(x);
    }
}

//stdmath.hpp
#ifndef STDMATH_HPP
#define STDMATH_HPP
namespace stdmath {
    double sin(double);
}
#endif

//uses_stdmath.cpp
#include <iostream>
#include "stdmath.hpp"

double sin(double x)
{
    return 1.0;
}

int main()
{
    std::cout << stdmath::sin(1) << std::endl;
    std::cout << sin(1) << std::endl;
}

コンパイラーの巧妙さによっては、追加の関数呼び出しによるオーバーヘッドが発生する可能性があると思います。

3
James

これは、この問題の解決を開始するための謙虚な試みにすぎません。 (提案を歓迎します。)

私はこの問題に長い間取り組んできました。問題が非常に明白であったケースは、このケースです。

#include<cmath>
#include<iostream>

namespace mylib{
    std::string exp(double x){return "mylib::exp";}
}

int main(){
    std::cout << std::exp(1.) << std::endl; // works
    std::cout << mylib::exp(1.) << std::endl; // works

    using namespace mylib;
    std::cout << exp(1.) << std::endl; //doesn't works!, "ambiguous" call
    return 0;
}

これは私の意見では厄介なバグであるか、少なくとも非常に不幸な状況です。 (少なくともGCCでは、Linuxではclang(GCCライブラリを使用)。)

最近、私はそれに別のショットを与えました。 (GCCの)cmathを見ると、ヘッダーは単にC関数をオーバーロードし、プロセスで名前空間を台無しにするためにあるようです。

namespace std{
   #include<math.h>
}
//instead of #include<cmath>

それでこれは動作します

using namespace mylib;
std::cout << exp(1.) << std::endl; //now works.

これは#include<cmath>と完全に同等ではないとほぼ確信していますが、ほとんどの関数は機能しているようです。

何よりも悪いのは、最終的には依存ライブラリが#inclulde<cmath>になることです。そのため、私はまだ解決策を見つけることができませんでした。

[〜#〜] note [〜#〜]:言うまでもなく、これはまったく機能しません

namespace std{
   #include<cmath> // compile errors
}
1
alfC