[C++]constについて

[戻る]
reshia [HomePage] 2005/03/02(水) 20:27:38
以下のようなプログラムを作りました
//-----------------------------------------
class Hoge {
 public:
  Hoge();
  int a;
  int GetA(int* const b);
};

Hoge::Hoge()
{
  a = 33;
}

int Hoge::GetA(int* b)
{
  b = &a;
  return a;
}

int main()
{
  Hoge o;
  int* c = new int;
  *c = 10;
  o.GetA(c);
  return 0;
}
//-----------------------------------------
これ、Hoge::GetAの宣言と実装でシグネチャが違うんですけど、
VC7のコンパイラでも、gcc3.4.2でもエラーも警告も出ないのですが、
私、何か考え違いしてますか?

const int* piyo;
は、ポインタpiyoの指す先の値の変更禁止

int* const piyo;
は、ポインタpiyoのアドレスを変更禁止
ですよね?
それぞれわざと、変更するようなコードを書いて確かめましたし・・・。

後者のconstって元々シグネチャに入らないものなのでしょうか?
前者のconstは、コンパイラがエラーを出してくれましたが・・・。

ついでですけど、それぞれの呼び名ってありますか?
よろしくお願いします。

シャノン 2005/03/02(水) 23:57:52
#規格書は読んでいないので、正確ではないかもしれません

お試しコード

int i = 0;
const int * pci = &i;
int * const pic = &i;

std::cout << typeid( pci ).name() << std::endl;
std::cout << typeid( pic ).name() << std::endl;

結果

int const *
int *

> 後者のconstって元々シグネチャに入らないものなのでしょうか?

そのようですね。
前者は「int const という型」へのポインタ。
後者は「int という型」へのポインタ。ただし変更不能。
ポインタの型は、それが指し示す先の型と同一(という言い方は変だけど、他にいい言い
方がないかな…)と考えれば、後者の const は型の一部ではないと考えられます。

処理系内部では const int ではなく int const としているのも、なかなか興味深い事実
です。
ここからは「その変数が変更不能であることを示す const は、基本的に型名の後ろに書
く」と読み取れます。
int const と int * const の const は、位置も役割も同じですね。
にも関わらず、前者の const は型情報の一部として扱われ、後者は扱われないのは、や
はり「ポインタの型は、それが指し示す先の型と同一」ということに起因するのでしょう
か。

Blue 2005/03/03(木) 00:00:41
解答ではないですが、

私の環境(WindowsXp VC++6.0)で

プロトタイプ宣言を
void func( void* const p );
として、実態を
void func( void* p ) {}
void func( void* const p ) {}
とすると、下のfuncが"実体を持っている"とエラーが出ます。
(関数 'void __cdecl func(void *const )' はすでに実体を持っています。)

次に
プロトタイプ宣言を
void func( void* p );
void func( void* const p );
として、実態を
void func( void* p ) {}
void func( void* const p ) {}
とすると、やはりエラーがでます。
(関数 'void  __cdecl func(void *)' はすでに実体を持っています。)

引数を同じ型としてみているのでしょうか。

Blue 2005/03/03(木) 00:08:17
>std::cout << typeid( pci ).name() << std::endl;
>std::cout << typeid( pic ).name() << std::endl;
typeidの手がありましたか。あんまつかわんから存在忘れてた。

私の環境でも同じ結果になりました。
ついでに const int* const も試してみたけど 結局 int const* でした。

Blue 2005/03/03(木) 00:32:26
関数ポインタでtypedefすると違う型になりますね。
そこらへんは翻訳の仕方が違うんだと思うけど。

void func( char* const p );
typedef void ( *FUNC )( char* );
int main( void ){
    FUNC f = func; // エラー
    return 0;
}
void func( char* p ){} // こっちはエラーでない

Blue 2005/03/03(木) 00:41:26
typeidで調べたら違う値でした。(レス1人で増やしてスマソ)

#include <iostream>
typedef void ( *FUNC1 )( char* );
typedef void ( *FUNC2 )( char* const );
typedef void ( *FUNC3 )( const char* );
typedef void ( *FUNC4 )( const char* const );

int main( void ){
    FUNC1 f1;
    FUNC2 f2;
    FUNC3 f3;
    FUNC4 f4;
    std::cout << typeid( f1 ).name() << std::endl;
    std::cout << typeid( f2 ).name() << std::endl;
    std::cout << typeid( f3 ).name() << std::endl;
    std::cout << typeid( f4 ).name() << std::endl;
    return 0;
}
結果
void (__cdecl*)(char *)
void (__cdecl*)(char * const)
void (__cdecl*)(char const *)
void (__cdecl*)(char const * const)

ありゅ 2005/03/03(木) 01:26:39
http://www.biwa.ne.jp/~chu0296/laboratory/huroku/const.html

いやぁ勉強になりました。こんなこと考えもしなかった。
皆さんありがとうございます。

REE 2005/03/03(木) 09:00:11
ポインタでなくても、int とconst intでも同様ですね。
どちらにしても引数は値渡しなので、関数を呼び出す側にとっては無関係ですね。
シグネチャに入らない理由も分かる気がします。

tetrapod 2005/03/03(木) 09:10:39
規格書は次のように言っています (ISO/IEC 14882:1998)
8.3.5 Declarator-Functions 3
仮引数型を修飾する cv-qualifier は削除される。
例: void(*)(const int) は void(*)(int) になる
このような cv-qualifier は関数本体中における仮引数の定義にのみ影響を及ぼし、
関数の型には影響を及ぼさないから。

よって Blue さん 2005/03/03(木) 00:00:41
>引数を同じ型としてみているのでしょうか。
同じ型と「みなさなければならない」ので当然です。

同じく Blue さん 2005/03/03(木) 00:41:26
>typeidで調べたら違う値でした。(レス1人で増やしてスマソ)
どのコンパイラで試したでしょうか?規格書によれば、これは一致しなければなりません。
bcc-5.5.1, gcc-3.3.4 では f1 f2 は一致 f3 f4 は一致します。
vc++6 は規格書より古い処理系なので、規格書に一致しないふるまいをするのはまあ仕方あり
ません。

Blue 2005/03/03(木) 09:13:39
>Blue 2005/03/03(木) 00:00:41
>私の環境(WindowsXp VC++6.0)で
です。

あまりに無知すぎる自分に反省。

reshia [HomePage] 2005/03/03(木) 11:26:33
[[解決]]
どうも、レスありがとうございます。

REEさんの「呼び出す側には無関係」というのはわかりますが、
それ以前に、実装と定義で型が違うというのが気持ち悪いのです。
それに今回のプログラムでは、
>int GetA(int* const b);
と宣言して、
>int Hoge::GetA(int* b)
>{
>  b = &a;
>  return a;
>}
として実装しているので(もちろんわざと)、
引数のポインタbの指すアドレスを関数内部で変更しても
コンパイラはエラーも警告も出してくれない。

原因は、
>このような cv-qualifier は関数本体中における仮引数の定義にのみ影響を及ぼし、
>関数の型には影響を及ぼさないから。
という規格のせいだということですよね。
でも警告くらいは出して欲しい、と思うのですよ・・・
警告を出す出さないって規格では決められていませんよね?

(もう既に冗長になってしまっているので解決チェックしておきますけど
 これについて何かご意見をお願いします)

古いスレッドには追加発言できません。