8

さまざまな算術型を半精度浮動小数点型 (uint16_t最低レベルの a のみ) に変換する関数があり、SFINAE と を使用して、整数型と浮動小数点型のさまざまな関数がありstd::enable_ifます。

template<typename T>
uint16_t to_half(typename std::enable_if<
                 std::is_floating_point<T>::value,T>::type value)
{
    //float to half conversion
}

template<typename T>
uint16_t to_half(typename std::enable_if<
                 std::is_integral<T>::value,T>::type value)
{
    //int to half conversion
}

これらは、明示的なインスタンス化によって、ユニバーサル テンプレート コンストラクターから内部的に呼び出されます。

template<typename T>
half::half(T rhs)
    : data_(detail::conversion::to_half<T>(rhs))
{
}

これはコンパイルされ、問題なく動作します。ここで、2 番目の関数を 2 つの関数に置き換えることで、符号付き整数と符号なし整数を区別しようとします。

template<typename T>
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value &&
                 std::is_signed<T>::value,T>::type value)
{
    //signed to half conversion
}

template<typename T>
uint16_t to_half(typename std::enable_if<std::is_integral<T>::value &&
                 std::is_unsigned<T>::value,T>::type value)
{
    //unsigned to half conversion
}

しかし、このVS2010をコンパイルしようとすると、

エラー C2995: "uint16_t math::detail::conversion::to_half( std::enable_if<std::tr1::is_integral<_Ty>::value && std::tr1::is_signed<_Ty>::value, T>::type )": 関数テンプレートは既に定義されています。

したがって、2 つのテンプレートを区別することはできないようですが、整数バージョンと浮動小数点バージョンでは明らかに問題はありませんでした。

しかし、私はそれほどテンプレートの魔術師ではないので、ここで明らかな何かが欠けている可能性があります (または、実際には機能するはずで、VS2010 のバグにすぎない可能性があります)。では、なぜこれが機能しないのでしょうか?また、プログラミングのオーバーヘッドをできるだけ少なくし、標準のみの機能の制限内で (可能であれば)、どのように機能させることができるのでしょうか?

4

3 に答える 3

8

個人的には、オーバーロードで同じことを達成できるので、ここではSFINAEをできるだけ避けます。

template<typename T>
uint16_t to_half_impl(T val, std::true_type, std::true_type)
{
    // is_integral + is_signed implementation
}

template<typename T>
uint16_t to_half_impl(T val, std::true_type, std::false_type)
{
    // is_integral + is_unsigned implementation
}

template<typename T>
uint16_t to_half_impl(T val, std::false_type, std::true_type)
{
    // is_floating_point implementation
}

template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value, uint16_t>::type to_half(T val)
{
    return to_half_impl(val, std::is_integral<T>(), std::is_signed<T>());
}
于 2012-02-14T23:28:11.653 に答える
3

これが機能しない場合は、コンパイラにエラーがあります。

式を含む2つの関数定義が1つの定義規則を満たす場合、テンプレートパラメータを含む2つの式は同等であると見なされます...

これがここで考慮すべき最も重要なルールです(「...」の詳細は省略)。2つのテンプレートは、トークンシーケンスが異なるため、ODRを満たしていません。

2つの関数テンプレートは、同じスコープで宣言され、同じ名前を持ち、同じテンプレートパラメーターリストを持ち、テンプレートパラメーターを含む式を比較するために上記のルールを使用して同等のリターンタイプとパラメーターリストを持っている場合、同等です。

したがって、2つのテンプレートは異なるテンプレートを定義し、衝突しません。これで、テンプレートが「機能的に同等」であるかどうかを確認できます。テンプレート引数の可能なセットに対して、enable_if式が常に同じ値を生成する場合になります。is_unsignedしかし、それはとには当てはまらないので、これも当てはまりis_signedません。もしそうなら、あなたのコードは形式が正しくありませんが、診断を必要としません(これは事実上「未定義の振る舞い」を意味します)。

于 2012-02-14T23:27:56.853 に答える