none
ポインタの参照を関数に渡す場合の書式 RRS feed

  • 質問

  • お世話になります。

    ある関数に「ポインタの参照」を渡すケースについて考えています。

    void foo()
    {
        int nTest = 10;
        int *pnTest = &nTest;

        func1(pnTest);
        // func2(pnTest);
        // func3(pnTest);
        // func4(pnTest);
    }

    void func1(int *& pnVal)
    {
        // ポインタ自身も参照先の値も変更できる
        *pnVal = 20;
        pnVal = NULL;
    }

    void func2(int const* const& pnVal)
    {
        // ポインタ自身も参照先の値も変更できない
        *pnVal = 20;  // コンパイルエラー!
        pnVal = NULL; // コンパイルエラー!
    }

    void func3(int * const& pnVal)
    {
        // 参照先の値は変更できるが、ポインタ自身は変更できない
        *pnVal = 20;
        pnVal = NULL; // コンパイルエラー!
    }


    ここまでは期待通りの結果なのですが、
    以下のようにするとコンパイルエラーが表示されます。

    void func4(int const*& pnVal)
    {
        // ポインタ自身は変更できるが、参照先の値は変更できない
        pnVal = NULL;
        *pnVal = 20; // コンパイルエラー!
    }

    error C2664: 'func4' : 1 番目の引数を 'int *' から 'const int *&' に変換できません。

    「ポインタ自身は変更できるが参照先の値は変更できない」ような関数の
    プロトタイプ宣言はどのように記述すればよいのでしょうか?

    2010年7月7日 6:41

回答

  • すみません。2003では何もいいませんが、2008でやってみたら確かにerrになりますね。
    エラーを出さないためにはconst修飾と&の参照渡しをどのように処理するか
    明確に指定しなければならないようです。

    >のようにキャストする必要があるのでしょうか?

    キャストでも十分ですが、本来の意味的には以下のコードと同等処理になるので、
    テンプレートで吸収するか、キャスト専門のクラスを挟むしかないかもしれません。

    typedef int const* CPINT;

    void func4( CPINT & pnVal)
    {
       pnVal = NULL;
    // *pnVal = 20; // コンパイルエラー!
        int nXXX = 0; // pnValとは無関係のコード
    }

    void foo()
    {
        int    nTest = 10;
        CPINT  p = &nTest;
        func4( p);
    }

    • 回答の候補に設定 山本春海 2010年7月13日 5:36
    • 回答としてマーク 山本春海 2010年7月14日 8:07
    2010年7月8日 1:08
  • 2008ですが・・・以下のコードで通りましたよ。

    void func4(int const*& pnVal)
    {
     // ポインタ自身は変更できるが、参照先の値は変更できない
     pnVal = NULL;
     //*pnVal = 20; // コンパイルエラー!
    }

    void foo()
    {
     int nTest = 10;
     const int *pnTest = &nTest;
     func4(pnTest);
    }

    • 回答の候補に設定 山本春海 2010年7月13日 5:36
    • 回答としてマーク 山本春海 2010年7月14日 8:07
    2010年7月8日 1:45
  • 関数側ではconst int(のポインタの参照)だと(=中の値は変化しない)思っているのに実態がconstで無い場合、書き変ってしまう恐れがあるのでエラーになるのだと思います。

    constは書き換えられない以外に、変化しないということも保証しています。

    if (a == 0) {
     処理1
    }
    if (a == 0) {
     処理2
    }
    と、
    if (a == 0) {
     処理1
     処理2
    }
    では、aがconstであるなら同じといえますが、
    aがconstで無いなら同じでない(処理1や非同期で動作している部分でaが変化するかもしれないので)となります。
    (つまり、constをはずすキャストは非常に危険だということです。)

    2010年7月8日 6:35

すべての返信

  • >「ポインタ自身は変更できるが参照先の値は変更できない」ような関数の
    >プロトタイプ宣言はどのように記述すればよいのでしょうか?

    func4( int const*& pnVal) で、要望通りにできてますよね(^^)b。
    {
      pnVal = NULL;
      *pnVal = 20; // コンパイルエラー!
    }
    の順番が他の関数と逆さまなので勘違いしましたか?

    2010年7月7日 7:37
  • コメントありがとうございます。

    説明の仕方が悪かったみたいで申し訳ありません。
    func4()の場合には、以下のようにしてもコンパイルエラーになります。
    ちなみに、VC++2010 Proでコンパイルしています。

    void foo()
    {
        int nTest = 10;
        int *pnTest = &nTest;

        func4(pnTest);
    }

    void func4(int const*& pnVal)
    {
        int nXXX = 0; // pnValとは無関係のコード
    }

    これでもfoo()の中の「func4(pnTest);」の呼び出して以下のエラーになります。

    error C2664: 'func4' : 1 番目の引数を 'int *' から 'const int *&' に変換できません。

    やはり、func4の場合だけは呼び出し元で「foo((int const*&) pnTest);」
    のようにキャストする必要があるのでしょうか?

     

    2010年7月7日 23:14
  • すみません。2003では何もいいませんが、2008でやってみたら確かにerrになりますね。
    エラーを出さないためにはconst修飾と&の参照渡しをどのように処理するか
    明確に指定しなければならないようです。

    >のようにキャストする必要があるのでしょうか?

    キャストでも十分ですが、本来の意味的には以下のコードと同等処理になるので、
    テンプレートで吸収するか、キャスト専門のクラスを挟むしかないかもしれません。

    typedef int const* CPINT;

    void func4( CPINT & pnVal)
    {
       pnVal = NULL;
    // *pnVal = 20; // コンパイルエラー!
        int nXXX = 0; // pnValとは無関係のコード
    }

    void foo()
    {
        int    nTest = 10;
        CPINT  p = &nTest;
        func4( p);
    }

    • 回答の候補に設定 山本春海 2010年7月13日 5:36
    • 回答としてマーク 山本春海 2010年7月14日 8:07
    2010年7月8日 1:08
  • 2008ですが・・・以下のコードで通りましたよ。

    void func4(int const*& pnVal)
    {
     // ポインタ自身は変更できるが、参照先の値は変更できない
     pnVal = NULL;
     //*pnVal = 20; // コンパイルエラー!
    }

    void foo()
    {
     int nTest = 10;
     const int *pnTest = &nTest;
     func4(pnTest);
    }

    • 回答の候補に設定 山本春海 2010年7月13日 5:36
    • 回答としてマーク 山本春海 2010年7月14日 8:07
    2010年7月8日 1:45
  • みなさん、アドバイスありがとうございました。

    お二人のご意見をまとめると、やはり呼び出し側で「const」を付ける必要がありそうですね。

    とても参考になりました。

    また、何かありましたらよろしくお願い致します。

     

    2010年7月8日 6:20
  • 関数側ではconst int(のポインタの参照)だと(=中の値は変化しない)思っているのに実態がconstで無い場合、書き変ってしまう恐れがあるのでエラーになるのだと思います。

    constは書き換えられない以外に、変化しないということも保証しています。

    if (a == 0) {
     処理1
    }
    if (a == 0) {
     処理2
    }
    と、
    if (a == 0) {
     処理1
     処理2
    }
    では、aがconstであるなら同じといえますが、
    aがconstで無いなら同じでない(処理1や非同期で動作している部分でaが変化するかもしれないので)となります。
    (つまり、constをはずすキャストは非常に危険だということです。)

    2010年7月8日 6:35