9

次のプログラム例を見てみましょう。

#include <cmath>

namespace half_float
{
    template<typename T> struct half_expr {};

    struct half : half_expr<half>
    {
        operator float() const;
    };

    template<typename T> half sin(const half_expr<T>&);
    template<typename T> half atan2(const half_expr<T>&, const half_expr<T>&);
}

using namespace std;
using half_float::half;

int main()
{
    half a, b;
    half s = sin(a);
    half t = atan2(a, b);
}

VS 2010では、これは問題なくコンパイルされます (現時点では明らかなリンカー エラーは無視してください)。しかし、VS 2012では次のようになります。

エラー C2440: 'conversion': 'float' から 'half_float::half' に変換できません

したがって、オーバーロードの解決は名前空間からバージョンを選択するのではなくhalf_float(ADL が達成する必要があります)、std暗黙的な変換を使用してfloat. しかし、奇妙なことに、これは通話に対してのみ発生し、atan2通話に対しては発生しませんsin

halfこのエラーが実際に最初に発生した大規模なプロジェクトでは、 のような他の 2 引数関数 (または 2 引数を持つ関数) でも発生しますが、1 引数関数では発生しませんfmod。同様に、より大きなプロジェクトでは、gcc 4.6/4.7およびclang 3.1でもエラーなく正常に動作しますが、この SSCCE バージョンを明示的にテストしていません。

したがって、私の質問は、VS 2012側でのこの誤った動作ですか ( 2012でのみ発生し、2 引数の関数でのみ発生することを考えると)、それともオーバーロード解決ルールのいくつかの微妙な点を監視しましたか (これは実際に少しトリッキーだと思います)?

編集:using namespace half_float直接または全体をグローバル名前空間に直接配置した場合にも発生します。同様に、私がそうでない場合にも発生しますusing namespace stdが、これは数学関数をグローバル名前空間に配置する VS 実装です。

編集:元のVC 2012コンパイラと2012 年 11 月の CTPの両方で発生します。

編集:厳密な意味で本当に標準に違反しているかどうかは完全にはわかりませんが少なくとも 1-引数は機能し、VSチームによるさらなる調査に値します。

4

3 に答える 3

6

I think I found the cause. The C++ standard says in section 26.8 [c.math], that for the mathematical functions of the C library,

there shall be additional overloads sufficient to ensure:

  1. If any argument corresponding to a double parameter has type long double, then all arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arguments corresponding to double parameters are effectively cast to float.

これは、 atan2 のドキュメントでも確認できます。

これらのオーバーロードは、次の形式の一般的な関数テンプレートを使用してVS 2012によって提供されます。

template<typename T,typename U> common_float_type<T,U>::type atan2(T, U);

したがって、インスタンス化に ( からhalf&へのconst half_expr<half>&) 暗黙的な変換が含まれるテンプレート関数と、直接インスタンス化できるテンプレート関数があります。したがって、後者が好ましい。これは、引数が 1 つの関数では発生しません。これらの関数では、 of を使用する関数に対してのみ VS 2012 によって提供される整数引数の一般的なバージョンのみが必要なためですstd::enable_ifstd::is_integral

しかし、これらの「追加のオーバーロード」が組み込み型に対してのみ提供されるという事実について、標準は少し不明確だと思います。したがって、VS 2012が過度に一般的な関数で厳密に標準に違反しているのか、それともそれらを提供する実行可能な実装オプションなのかはまだわかりません。

編集:どうやら、標準の不明確な文言に対する欠陥レポート 2086が既にあり、修正が進行中であり、これらの追加のオーバーロードの要件を算術型のみに制限しています。これは常に元の意図 (そしてほぼすべての既存の実装で実現されている) であり、不明瞭な表現は単なる文言だったので、私はこれをVS 2012の実装のバグと見なします。

于 2013-01-12T23:09:19.270 に答える
0

あなたのコードを試してみたところ、何が問題なのかがわかりました。

half::sinと を実装していないためhalf::atan2、リンカはとにかくエラーをスローします。したがって、メソッドhalf::sinとを実装すれば、それでhalf::atan2解決するはずです (もちろん、無意味な空の半分を返すように実装しました)。

その手順 (2 つの必須メソッドの (無意味な) 実装を提供する) を行った後、エラー メッセージはほとんど魔法のように消えました。

VSではなくGCCを使用しているため、これは問題の解決策ではないかもしれません。


編集: G++ で使用したサンプルを Visual Studio で試してみたところ、より奇妙なエラー メッセージが表示されました。エラーの奇妙さと GCC で動作するコードを考えると、これは VC2012 のバグであると結論付けなければなりません。

于 2013-01-12T17:58:50.023 に答える
0

回避策は、SFINAE が の VS2012 バージョンを取り除くように、特殊化して未定義の型にする_Common_float_typeことhalfです。half_expratan2

namespace std {
    template<class T1, class T2>
    struct _Common_float_type<half_float::half_expr<T1>, half_float::half_expr<T2>>;
    template<class T2>
    struct _Common_float_type<half_float::half, half_float::half_expr<T2>>;
    template<class T1>
    struct _Common_float_type<half_float::half_expr<T1>, half_float::half>;
    template<>
    struct _Common_float_type<half_float::half, half_float::half>;
}

テンプレートの特殊化では基本クラスが考慮されないため、halfとの 4 つの組み合わせすべてを特殊化する必要があることに注意してください。half_expr

于 2013-04-23T11:09:56.250 に答える