6

コンパイル時に、引数の型に応じて整数または浮動小数点のモジュロを計算する次の関数を検討してください。

template<typename T>
constexpr T modulo(const T x, const T y)
{
    return (std::is_floating_point<T>::value) ? (x < T() ? T(-1) : T(1))*((x < T() ? -x : x)-static_cast<long long int>((x/y < T() ? -x/y : x/y))*(y < T() ? -y : y))
    : (static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(x)
      %static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(y));
}

この関数の本体を改善できますか? (整数型と浮動小数点型の両方に対して単一の関数が必要です)。

4

6 に答える 6

5

これをクリーンアップする 1 つの方法を次に示します。

#include <type_traits>
#include <cmath>

template <typename T>  //     integral?       floating point?
bool remainder_impl(T a, T b, std::true_type, std::false_type) constexpr
{
    return a % b;  // or whatever
}

template <typename T>  //     integral?        floating point?
bool remainder_impl(T a, T b, std::false_type, std::true_type) constexpr
{
    return std::fmod(a, b); // or substitute your own expression
}

template <typename T>
bool remainder(T a, T b) constexpr
{
    return remainder_impl<T>(a, b,
             std::is_integral<T>(), std::is_floating_point<T>());
}

算術でない型でこの関数を呼び出そうとすると、コンパイラ エラーが発生します。

于 2013-01-12T16:17:21.340 に答える
2

私はむしろこのように定義したいと思います (テンプレートのエイリアス + テンプレートのオーバーロード):

#include <type_traits>

using namespace std;

// For floating point types

template<typename T, typename enable_if<is_floating_point<T>::value>::type* p = nullptr>
constexpr T modulo(const T x, const T y)
{
    return (x < T() ? T(-1) : T(1)) * (
            (x < T() ? -x : x) -
            static_cast<long long int>((x/y < T() ? -x/y : x/y)) * (y < T() ? -y : y)
            );
}

// For non-floating point types

template<typename T>
using TypeToCast = typename conditional<is_floating_point<T>::value, int, T>::type;

template<typename T, typename enable_if<!is_floating_point<T>::value>::type* p = nullptr>
constexpr T modulo(const T x, const T y)
{
    return (static_cast<TypeToCast<T>>(x) % static_cast<TypeToCast<T>>(y));
}

int main()
{
    constexpr int x = modulo(7.0, 3.0);
    static_assert((x == 1.0), "Error!");
    return 0;
}

より長くなりますが、よりクリーンな IMO です。「単一の関数」とは、「均一に呼び出すことができるもの」を意味すると想定しています。「単一の関数テンプレート」を意味する場合は、テンプレート エイリアスの改善を維持し、オーバーロードをそのままにしておきます。ただし、別の回答で述べたように、単一の関数テンプレートが必要な理由は明確ではありません。

于 2013-01-12T15:50:48.087 に答える
1
template <class T>
constexpr
T
modulo(T x, T y)
{
    typedef typename std::conditional<std::is_floating_point<T>::value,
                                        int,
                                        T
                                     >::type Int;
    return std::is_floating_point<T>() ?
              x - static_cast<long long>(x / y) * y :
              static_cast<Int>(x) % static_cast<Int>(y);
}
于 2013-01-12T16:49:20.820 に答える
1

あなたが尋ねる、

「この機能の本体を改善できないか?」

そうです。今、それはスパゲッティの混乱です:

template<typename T>
constexpr T modulo(const T x, const T y)
{
    return (std::is_floating_point<T>::value) ? (x < T() ? T(-1) : T(1))*((x < T() ? -x : x)-static_cast<long long int>((x/y < T() ? -x/y : x/y))*(y < T() ? -y : y))
    : (static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(x)
      %static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(y));
}

あなたはそれを明確にします…

「(整数型と浮動小数点型の両方に単一の関数が必要です)」

テンプレート単一の機能ではありません。テンプレートです。そこから関数が生成されます。

これは、あなたの質問が誤った仮定に基づいていることを意味します。

その前提を取り除いた上で、関数本体を単純化する 1 つの方法は、当然のことながら、テンプレートを浮動小数点型と他の数値型に特化することです。これを行うには、関数テンプレートの実装をクラスに配置します (C++ は関数の部分的な特殊化をサポートせず、クラスのみをサポートするため)。

次に、「0?0 : blah」トリックを含むさまざまな書式設定トリックを使用して、行やインデントなどを使用して関数を読みやすくすることができます。:-)


補遺: あなたのコードを掘り下げると、インボーカーの型を無計画にキャストし、それを無視してlong intいることがわかります。intそれはダメです。さまざまな引数の型と大きな値/小さな値を指定して関数を呼び出して、一連の自動化されたテスト ケースを作成することをお勧めします。

于 2013-01-12T15:48:22.897 に答える
0

もっと簡単なものがあると思います:

// Special available `%`
template <typename T, typename U>
constexpr auto modulo(T const& x, U const& y) -> decltype(x % y) {
    return x % y;
}

注: so の検出に基づいて%、演算子を実装している限り、カスタム型でも機能します。ついでにミックスタイプも作りました。

// Special floating point
inline constexpr float modulo(float x, float y) { return /*something*/; }

inline constexpr double modulo(double x, double y) { return /*something*/; }

inline constexpr long double modulo(long double x, long double y) { return /*something*/; }

注:fmod残念ながら、利用できるようにした方がクリーンになると思いますconstexpr。したがって、私は浮動小数点型の非テンプレート モジュロを選択しました。これにより、おそらく型のバイナリ表現に基づいて正確なモジュロを計算する魔法を実行できます。

于 2013-01-12T17:05:28.937 に答える