17

整数の累乗を返す関数を作りたいです。fmuecke の解決策を c++ の整数乗で読んでください。

ただし、彼の解決策を任意の型 T に一般化したいと考えています。c++11 には constexpr があるため、これは可能だと思います。

素朴に、私は次のようなことを試しました、

template<class T, int N>
inline constexpr T pow(const T x){
    return pow<N-1>(x) * x;
}
template<class T>
inline constexpr T pow<T, 1>(const T x){
    return x;
}
template<class T>
inline constexpr T pow<T, 0>(const T x){
    return 1;
}

関数テンプレートの部分的な特殊化は許可されていないため、実際にはこのアプローチは失敗しました。

そしてもう1つ質問です。コンパイル時に constexpr 関数を評価するかどうかは、コンパイラ次第だと聞きました。一般的なタイプの計算を強制するにはどうすればよいですか。整数 const の最も簡単なハックの 1 つは、それを std::integral_const::value でラップすることです。

4

4 に答える 4

19

関数テンプレートを部分的に特殊化する必要があることに気付いた場合 ( DyP の回答が示すように、この場合に必要であるという意味ではないことに注意してください)、オーバーロードに頼ることができます (この最後の更新を参照してください)。答え) または、それが不可能な場合は、その関数テンプレートをクラス テンプレートにラップし、元の関数テンプレート (およびその特殊化) を静的な非テンプレート メンバー関数に置き換えます。

namespace detail
{
    template<class T, int N>
    struct helper
    {
        static constexpr T pow(const T x){
            return helper<T, N-1>::pow(x) * x;
        }
    };

    template<class T>
    struct helper<T, 1> // Unnecessary specialization! (see the edit)
    {
        static constexpr T pow(const T x){
            return x;
        }
    };

    template<class T>
    struct helper<T, 0>
    {
        static constexpr T pow(const T x){
            return 1;
        }
    };
}

次に、ヘルパー クラス テンプレートの特殊化に委譲するヘルパー関数テンプレートを提供できます。

template<int N, class T>
T constexpr pow(T const x)
{
    return detail::helper<T, N>::pow(x);
}

これが実際のです。

編集:

特殊化 forN == 1は実際には必要ないことに注意してください。この回答の目的は主に、関数テンプレートを一般的に部分的に特殊化することの不可能性を回避する方法を示すことだったので、元のテキストに残しました。そのため、元のプログラムを少しずつ翻訳しました。

ただし、コメントでDyp が指摘したように、これで十分です。

namespace detail
{
    template<class T, int N>
    struct helper
    {
        static constexpr T pow(const T x){
            return helper<T, N-1>::pow(x) * x;
        }
    };

    template<class T>
    struct helper<T, 0>
    {
        static constexpr T pow(const T x){
            return 1;
        }
    };
}

アップデート:

さらに注意してください。関数テンプレートを特殊化できる場合でも(たとえば、部分的ではなく明示的な特殊化を使用して)、関数テンプレートの特殊化は通常のように動作しないため、通常はそうすることはお勧めできません。予想。

関数テンプレートの特殊化が必要と思われる状況のほとんどは、タグ ディスパッチなどのよく知られた手法を利用したオーバーロードによって実際に実現できます。Potatoswatterがコメントで例を提案し、この状況で使用できることを指摘しています。std::integral_constant

template<class T>
inline constexpr T pow(const T x, std::integral_constant<T, 0>){
    return 1;
}

template<class T, int N>
inline constexpr T pow(const T x, std::integral_constant<T, N>){
    return pow(x, std::integral_constant<T, N-1>()) * x;
}

template<int N, class T>
inline constexpr T pow(const T x)
{
    return pow(x, std::integral_constant<T, N>());
}

ただし、「関数テンプレートの部分的な特殊化が必要と思われる問題を解決する方法」に関するこれらすべてのガイドラインは、本当に必要な場合に考慮する必要があります。この具体的なケースでは、DyP が彼の回答で示したように、そうではありません。

于 2013-05-08T14:52:23.267 に答える
5

単一の関数を使用したソリューションを次に示します。

template <int N, class T> 
constexpr T pow(const T& x) 
{
    return N > 1 ? x*pow<(N-1)*(N > 1)>(x) 
                 : N < 0 ? T(1)/pow<(-N)*(N < 0)>(x) 
                         : N == 1 ? x 
                                  : T(1);
}
于 2014-05-17T23:58:08.047 に答える