9

Scott Meyers の「Effective C++」の項目 48 について簡単な質問があります。以下の本からコピーしたコードがわかりません。

    #include <iostream>
    using namespace std;

    template <unsigned n>
    struct Factorial
    {
       enum { value=n*Factorial<n-1>::value };
    };

    template <>
    struct Factorial<0>
    {
        enum { value=1};
    };

    int main()
    {
        cout<<Factorial<5>::value<<endl;
        cout<<Factorial<10>::value<<endl;
    }

テンプレート プログラミングで enum を使用する必要があるのはなぜですか? これを行う別の方法はありますか?事前に助けてくれてありがとう。

4

5 に答える 5

8

static const int以下も使用できます。

template <unsigned n>
struct Factorial
{
   static const int value= n * Factorial<n-1>::value;
};

template <>
struct Factorial<0>
{
   static const int value= 1;
};

これでもいいはずです。どちらの場合も結果は同じです。

std::integral_constantまたは、 (C++11 のみ) のような既存のクラス テンプレートを次のように使用することもできます。

template <unsigned n>
struct Factorial : std::integral_constant<int,n * Factorial<n-1>::value> {};

template <>
struct Factorial<0> : std::integral_constant<int,1> {};
于 2012-08-11T18:28:16.870 に答える
6

enum他の回答は代替アプローチを十分にカバーしていることがわかりますが、 (またはstatic const int) が必要な理由を誰も説明していません。

まず、次の非テンプレートの同等物を検討してください。

#include <iostream>

int Factorial(int n)
{
    if (n == 0)
        return 1;
    else
        return n * Factorial(n-1);
}

int main()
{
    std::cout << Factorial(5) << std::endl;
    std::cout << Factorial(10) << std::endl;
}

簡単に理解できるはずです。ただし、階乗の値が実行時に計算されるという欠点があります。つまり、プログラムを実行した後、コンパイラは再帰的な関数呼び出しと計算を実行します。

テンプレート アプローチの考え方は、コンパイル時に同じ計算を実行し、その結果を実行可能ファイルに配置することです。言い換えれば、あなたが提示した例は、似たようなものに解決されます:

int main()
{
    std::cout << 120 << std::endl;
    std::cout << 3628800 << std::endl;
}

しかし、それを達成するためには、コンパイラを「だまして」計算を実行させる必要があります。そのためには、結果をどこかに保存できるようにする必要があります。

enumまさにそれを行うためにあります。そこでうまくいかないことを指摘することで、それを説明しようとします。

通常の を使用しようとしてもint、非静的メンバーのようなものintはインスタンス化されたオブジェクトでのみ意味があるため、機能しません。そして、このように値を割り当てることはできませんが、代わりにコンストラクターでそれを行います。プレーンintは機能しません。

代わりに、インスタンス化されていないクラスでアクセスできるものが必要です。試すことはできますstatic intが、それでもうまくいきません。clang問題の非常に簡単な説明が得られます。

c.cxx:6:14: error: non-const static data member must be initialized out of line
                static int value=n*Factorial<n-1>::value ;
                           ^     ~~~~~~~~~~~~~~~~~~~~~~~

実際にこれらの定義を行外に配置すると、コードはコンパイルされますが、2 つ0の が生成されます。これは、この形式が値の計算をプログラムの初期化まで遅らせ、正しい順序を保証しないためです。Factorial<n-1>::value計算される前に s が取得されたため、0返された可能性があります。さらに、それはまだ私たちが実際に望んでいるものではありません。

最後に、static const intそこに置くと、期待どおりに機能します。これstatic constは、コンパイル時に計算する必要があるためであり、それがまさに私たちが望んでいることです。コードをもう一度入力してみましょう。

#include <iostream>

template <unsigned n>
struct Factorial
{
    static const int value=n*Factorial<n-1>::value ;
};

template <>
struct Factorial<0>
{
    static const int value=1;
};

int main()
{
    std::cout << Factorial<5>::value << std::endl;
    std::cout << Factorial<10>::value << std::endl;
}

最初にインスタンス化しますFactorial<5>static const intコンパイラーがその値をコンパイラー時に計算する必要があることを強制します。事実上、Factorial<4>別の値を計算する必要があるときに型をインスタンス化します。そして、これFactorial<0>以上インスタンス化せずに値を計算できる場所に到達するまで、これは 1 になります。

それで、それは別の方法と説明でした。コードを理解するのに少しでも役に立てば幸いです。

この種のテンプレートは、最初に投稿した再帰関数の代わりと考えることができます。以下を置き換えるだけです。

  • return x;static const int value = ...
  • f(x-1)t<x-1>::value
  • if (n == 0)専門化struct Factorial<0>

そして、enumそれ自体については、すでに指摘したように、 と同じ動作を強制するために例で使用されましたstatic const int。コンパイル時にすべてのenum値を知る必要があるため、事実上、要求されたすべての値をコンパイル時に計算する必要があるためです。

于 2012-08-11T23:21:00.687 に答える
2

より具体的に言うと、「enum ハック」が存在するstatic const intのは、当時の多くのコンパイラでそれを行うためのより正しい方法がサポートされていなかったためです。最新のコンパイラでは冗長です。

于 2012-08-11T21:53:05.870 に答える
1

static const intナワズが言うように使用できます。Scott Myers が列挙型を使用する理由は、静的な const 整数のクラス内初期化に対するコンパイラのサポートが、彼が本を書いたときに少し制限されていたためだと思います。したがって、列挙型はより安全なオプションでした。

于 2012-08-11T21:44:52.370 に答える