スレッド: [cppll:12985] std::map のキーにオブジェクトのインスタンスを使用する

スレッド

[cppll:12985] std::map のキーにオブジェクトのインスタンスを使用する

[ | ▲ / | ]
Subject:
[cppll:12985] std::map のキーにオブジェクトのインスタンスを使用する
From:
take_de_x <take_de_x@...>
Date:
Thu, 14 Jun 2007 14:04:18 +0900
X-Mailer:
Becky! ver. 2.29 [ja]
Message-Id:
<20070614140412.EE2A.TAKE_DE_X‐at‐nifty.com>
こんにちは。中山です。

std::mapの使用方法に関してご教示頂きたくメールする次第です。
下記のように

map< CLevelPos, InfoPtr > InfoTable;

「CLevelPos」クラスのオブジェクトを検索キーにして「InfoPtr」を保持・取得
できる変数「InfoTable」を宣言しました。

CLevelPosは下記のように2つの「short型」変数を持っています。
//ザックリと全メンバを「public」にしてますが実際は異なります。
class CLevelPos{
public:
   CLevelPos( short level_, short pos_ ) : level(level_), pos(pos_) {}
   unsigned short level;
   unsigned short pos;
};


そこで、下記の関数の中身の書き方に疑問を持ちまして質問させていただく次第
です。
bool CLevelPos::operator < (const CLevelPos &src ) const ;


当初、下記のように書いてみましたが思った通りに動作しませんでした。
具体的には、「InfoTable.find(oKey);」時に、全く見当はずれのKeyが「合致し
た」という結果を返してきました。

bool CLevelPos::operator < (const CLevelPos &src ) const {
   if( level < src.level ) return true;
   if( pos < src.pos ) return true;
   return false;
}


下記のように書き換えたところ、問題なく動作するようになりましたが、
検索キー用のオブジェクトである「CLevelPos」のメンバ変数が増えた場合に下
記の方法では限界がくるので、どのようにすべきかアドバイス頂きたくお願いい
たします。

bool CLevelPos::operator < (const CLevelPos &src ) const {
   // 二つの値を一つの値に変換して大小を比較する
   unsigned long d = ((level & 0xffff) << 16) | (pos & 0xffff);
   unsigned long s = ((src.level & 0xffff) << 16) | (src.pos & 0xffff);

   return d < s;
}


以上、長文失礼致しました。
アドバイスをいただければ有難く思います。
宜しくお願いいたします。


--
take_de_x <take_de_x@...>

[cppll:12986] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12986] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
とっちゃん(高萩 俊行) <tosiyuki@...>
Date:
Thu, 14 Jun 2007 14:17:29 +0900
X-Mailer:
HidemaruMail 4.69 (WinNT,600)
Message-Id:
<AAC7AE434E137Etosiyuki‐at‐hh.iij4u.or.jp>
In-Reply-To:
12985
References:
12985
とっちゃんです。

こちらでは、はじめましてですね。みなさんよろしく〜w

>
>当初、下記のように書いてみましたが思った通りに動作しませんでした。
>具体的には、「InfoTable.find(oKey);」時に、全く見当はずれのKeyが「合致し
>た」という結果を返してきました。
>
>bool CLevelPos::operator < (const CLevelPos &src ) const {
>    if( level < src.level ) return true;
>    if( pos < src.pos ) return true;
>    return false;
>}
>
mapや、set でキーとなるためというより、

obj1 < obj2 が成立する場合、
obj2 < obj1 が絶対に成立しないようにしなければなりませんが、
現在の内部比較条件だと成立する場合があります。なので、結果が不安定になってる。

if( level < src.level ) return true;
else if( level > src.level ) return false;

としてやれば、安定しますよ。
理由は分かりますよね?


// とっちゃん(高萩 俊行)
// http://blogs.wankuma.com/tocchann/
// Microsoft MVP for Windows-SDK (Oct 2005 - Sept 2007)
// WindowsInstallerの話題は http://www.freeml.com/ctrl/html/MLInfoForm/msi@freeml.com まで

[cppll:12990] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12990] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
take_de_x <take_de_x@...>
Date:
Thu, 14 Jun 2007 14:35:46 +0900
X-Mailer:
Becky! ver. 2.29 [ja]
Message-Id:
<20070614142754.EE2C.TAKE_DE_X‐at‐nifty.com>
In-Reply-To:
12986
References:
12985 12986
こんにちは。中山です。

皆さん、素早いレスポンスありがとうございます。

> obj1 < obj2 が成立する場合、
> obj2 < obj1 が絶対に成立しないようにしなければなりませんが、

仰るとおりです。
そのように operator < () を定義したかったのですが、自分の視野が狭かった
ようです^^;

map,set が検索を行う時は
・「X1 < X2」「X2 < X1」の両方が成立しなかったとき「X1==X2」とみなす
ってのが標準的な動作だと一応は認識はしていて(合ってますか?)、先のメール
のようなoperatorを定義したのですが、考えが足らなかったです。

メンバ変数が増えたときのことを考慮して下記のように書いておくのが良いでしょ
うか。

if( level < src.level ) return true;
else if( level > src.level ) return false;
return pos < src.pos;


例えば「value」が増えたら下記のように書き換えるとか…


if( level < src.level ) return true;
else if( level > src.level ) return false;
if( pos < src.pos ) return true;
else if( pos > src.pos ) return false;
return value < src.value;

皆さん、ありがとうございました。

--
take_de_x <take_de_x@...>

[cppll:12991] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12991] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
Akihiko Matuura <matuura@...>
Date:
Thu, 14 Jun 2007 14:53:34 +0900
X-Mailer:
Becky! ver. 2.31 [ja]
Message-Id:
<20070614143820.FE0E.MATUURA‐at‐core.co.jp>
In-Reply-To:
12990
References:
12986 12990
札幌の松浦です。

# 最近暑いっす、ほんとに札幌かよ?

> map,set が検索を行う時は
> ・「X1 < X2」「X2 < X1」の両方が成立しなかったとき「X1==X2」とみなす
> ってのが標準的な動作だと一応は認識はしていて(合ってますか?)、

はい、あっています。標準的というか標準です。

このあたりは、「プログラミング言語C++第3版」 17.1.4.1, 17.1.4.2
に詳しく書いてあります。


--
 松浦 明彦 <matuura@...>

[cppll:12994] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12994] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
take_de_x <take_de_x@...>
Date:
Thu, 14 Jun 2007 15:51:26 +0900
X-Mailer:
Becky! ver. 2.29 [ja]
Message-Id:
<20070614155033.EE2E.TAKE_DE_X‐at‐nifty.com>
In-Reply-To:
12991
References:
12990 12991
こんにちは。中山です。

> > map,set が検索を行う時は
> > ・「X1 < X2」「X2 < X1」の両方が成立しなかったとき「X1==X2」とみなす
> > ってのが標準的な動作だと一応は認識はしていて(合ってますか?)、
> はい、あっています。標準的というか標準です。
> このあたりは、「プログラミング言語C++第3版」 17.1.4.1, 17.1.4.2
> に詳しく書いてあります。

なるほど、ありがとうございます。
再勉強したいと思います。


--
take_de_x <take_de_x@...>

[cppll:12992] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12992] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
INADA Naoki <inadauer@...>
Date:
Thu, 14 Jun 2007 14:56:14 +0900
Message-Id:
<81dd9a9a0706132256k11e42e54mb3c443cd7a862382‐at‐mail.gmail.com>
In-Reply-To:
12990
References:
12985 12986 12990
稲田です。

蛇足ですが、else は意味が無いので、つけないほうが見た目がすっきりしますね。

if( level < src.level ) return true;
if( level > src.level ) return false;

さらに、operator < と operator > の両方を使う意味が無いので、 operator < に統一すると、
επιστημη さんの例の書き方になります。

ちょっとしたことですが、これができていると「センスが良い」と思います。

--
INADA Naoki <inadauer@...>
Blog: http://d.hatena.ne.jp/methane/ (sorry, Japanese only.)

[cppll:12996] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12996] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
take_de_x <take_de_x@...>
Date:
Thu, 14 Jun 2007 15:53:57 +0900
X-Mailer:
Becky! ver. 2.29 [ja]
Message-Id:
<20070614155150.EE30.TAKE_DE_X‐at‐nifty.com>
In-Reply-To:
12992
References:
12990 12992
こんにちは、中山です。

稲田さん、とっちゃんさん、ソースコードの記述方法に関してのアドバイスあり
がとうございました。
非常に勉強になりました。

見やすく、間違いが発生しにくいコードを書くように気をつけたいと思います。

--
take_de_x <take_de_x@...>

[cppll:12993] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12993] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
とっちゃん(高萩 俊行) <tosiyuki@...>
Date:
Thu, 14 Jun 2007 15:38:58 +0900
X-Mailer:
HidemaruMail 4.69 (WinNT,600)
Message-Id:
<ABC7AE4EAF9A82tosiyuki‐at‐hh.iij4u.or.jp>
In-Reply-To:
12990
References:
12985 12986 12990
とっちゃんです。

>
>if( level < src.level ) return true;
>else if( level > src.level ) return false;
>return pos < src.pos;
>
>
>例えば「value」が増えたら下記のように書き換えるとか…
>
>
>if( level < src.level ) return true;
>else if( level > src.level ) return false;
>if( pos < src.pos ) return true;
>else if( pos > src.pos ) return false;
>return value < src.value;
>

書き方として、else や operator<() で統一するかは、
コーディングスタイルなどもあるので、どちらでもいいとは思いますが、

構文そのものは全部統一しておくことをお勧めします。

上記の例でいえば、

if( level < src.level ) return true;
else if( level > src.level ) return false;

if( pos < src.pos ) return true;
else if( pos > src.pos ) return false;

if( value < src.value ) return true;
else if( value > src.value ) return false;

return false;

という感じですね。

επιστημηさん風にかくなら、

if( level < src.level ) return true;
if( src.level < level ) return false;

if( pos < src.pos ) return true;
if( src.pos < pos ) return false;

...

return false;

という感じ。

若干冗長な見た目になりますが、いまどきのコンパイラなら
この程度なら余裕で最適化してくれます。

近い将来のレベルで変更が予定されている(要するに作りかけのクラス)のなら
こういう形にしておくことで、単純にコピーして増やしていけますから
結構便利です。

また、構文を合わせることで、ソース上に同じパターンが出現するので
ミスを抑えやすくするという利点も生まれます。


// とっちゃん(高萩 俊行)
// http://blogs.wankuma.com/tocchann/
// Microsoft MVP for Windows-SDK (Oct 2005 - Sept 2007)
// WindowsInstallerの話題は http://www.freeml.com/ctrl/html/MLInfoForm/msi@freeml.com まで

[cppll:12997] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12997] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
FUKUDA, Fumiki <fukuda.fm@...>
Date:
Thu, 14 Jun 2007 15:53:39 +0900
X-Mailer:
Microsoft Office Outlook 11
Message-Id:
<000e01c7ae50$bcf618d0$4a65040a@fukudapd>
In-Reply-To:
12993
References:
12985 12986 12990 12993
επιστημηです。

> 構文そのものは全部統一しておくことをお勧めします。
>...
> if( level < src.level ) return true;
> if( src.level < level ) return false;
>
> if( pos < src.pos ) return true;
> if( src.pos < pos ) return false;
> ...
> return false;
>
> という感じ。
>
> 若干冗長な見た目になりますが、いまどきのコンパイラなら
> この程度なら余裕で最適化してくれます。

ですねー。
今後(近い将来)の拡張が見込まれてるならきっちり形式化しておくが吉でしょね。
メンバ追加時にどういぢればいいかがクリアだから。

[cppll:12995] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12995] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
cb <sumiaki@...>
Date:
Thu, 14 Jun 2007 15:53:34 +0900
X-Mailer:
Denshin 8 Go V32.1.5.3 on Windows 5.01.2600 Service Pack 2
Message-Id:
<200706140653.l5E6rYix018510‐at‐mx54.ms.so-net.ne.jp>
In-Reply-To:
12990
References:
12985 12986 12990
cbと申します。お久しぶりです。

横から質問ですみません。


私の場合、比較演算子のオーバロードはメンバ関数ではなくフレンド関数にします。

bool CLevelPos::operator < (const CLevelPos &src ) const {
       if ( level < src.level ) return true;
       if ( src.level < level ) return false;
       return pos < src.pos;
}

CLevelPos {
       /* 略 */
       friend bool operator < ( const CLevelPos& srcL, const CLevelPos& srcR );
}
bool operator < ( const CLevelPos& srcL, const CLevelPos& srcR ) {
       if ( srcL.level < srcR.level ) return true;
       if ( srcR.level < srcL.level ) return false;
       return srcL.pos < srcR.pos;
}


メンバ関数とフレンド関数でのそれぞれのメリット、デメリットなどありますでしょうか?

[cppll:12998] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12998] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
FUKUDA, Fumiki <fukuda.fm@...>
Date:
Thu, 14 Jun 2007 15:57:19 +0900
X-Mailer:
Microsoft Office Outlook 11
Message-Id:
<000f01c7ae51$403d9470$4a65040a@fukudapd>
In-Reply-To:
12995
References:
12985 12986 12990 12995
> メンバ関数とフレンド関数でのそれぞれのメリット、デメリットなどありますで
しょうか?

演算子の両辺が異なる型のとき、メンバ関数では対応できなくなることがあります。

class String {
public:
 bool operator==(const char* rhs);
 ...
};

なんて定義だと x == "ほげ" できるけど "ほげ" == x できんです。

[cppll:13000] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:13000] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
cb <sumiaki@...>
Date:
Fri, 15 Jun 2007 19:47:21 +0900
X-Mailer:
Denshin 8 Go V32.1.5.3 on Windows 5.01.2600 Service Pack 2
Message-Id:
<200706151047.l5FAlMSR025729‐at‐mx55.ms.so-net.ne.jp>
In-Reply-To:
12998
References:
12985 12986 12990 12995 12998
cbです。


> 演算子の両辺が異なる型のとき、メンバ関数では対応できなくなることがあります。
>
> class String {
> public:
>   bool operator==(const char* rhs);
>   ...
> };
>
> なんて定義だと x == "ほげ" できるけど "ほげ" == x できんです。
>
ああ、ありましたね。

bool operator == ( const String& srcL, const char& srcR ) { }
bool operator == ( const char* srcL, const String& srcR ) { }
としておけば、両方に対応できるってやつですね。

[cppll:12999] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12999] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
Masahiro Kasahara <mkasa@...>
Date:
Fri, 15 Jun 2007 19:26:31 +0900
Message-Id:
<16c6fbcb0706150326s431d2df4v4600516e29f7d0ec‐at‐mail.gmail.com>
In-Reply-To:
12990
References:
12985 12986 12990
笠原、です。

On 6/14/07, take_de_x <take_de_x@...> wrote:
> メンバ変数が増えたときのことを考慮して下記のように書いておくのが良いでしょ
> うか。
>
> if( level < src.level ) return true;
> else if( level > src.level ) return false;
> return pos < src.pos;

少しわき道に逸れますが、メンバー変数 A, B, C ...., Z があるときには
operator == が定義されていれば

if( A != src.A ) return A < src.A;
if( B != src.B ) return B < src.B;


if( Z != src.Z ) return Z < src.Z;
return false;

という書き方がコンパクトで、変数の追加削除に強いのでお勧めです。
operator < しかない場合には επιστημη さん方式でないと無理ですが。

--
KASAHARA Masahiro <mkasa@...>

[cppll:12987] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12987] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
cppll‐at‐denchu.jp <cppll@...>
Date:
Thu, 14 Jun 2007 14:19:07 +0900 (JST)
X-Mailer:
FreeML Web Mailer XP; SP2
Message-Id:
<9270459.1181798347197@www1>
In-Reply-To:
12985
References:
12985
 こんにちは、電柱一家です。

> bool CLevelPos::operator < (const CLevelPos &src ) const {
>     if( level < src.level ) return true;
>     if( pos < src.pos ) return true;
>     return false;
> }

 これだと、level > src.level のとき、 pos < src.pos で true
 を返すからまずいのかな?
 if ( level < src.level )
  return true;
 if ( level == src.level && pos < src.pos )
  return true;
 とか、かな?

[cppll:12988] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / | ]
Subject:
[cppll:12988] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
FUKUDA, Fumiki <fukuda.fm@...>
Date:
Thu, 14 Jun 2007 14:18:52 +0900
X-Mailer:
Microsoft Office Outlook 11
Message-Id:
<000b01c7ae43$7f8d7f90$4a65040a@fukudapd>
In-Reply-To:
12985
References:
12985
επιστημηです。

> 当初、下記のように書いてみましたが思った通りに動作しませんでした。
> 具体的には、「InfoTable.find(oKey);」時に、全く見当はずれのKeyが「合致し
> た」という結果を返してきました。
>
> bool CLevelPos::operator < (const CLevelPos &src ) const {
>     if( level < src.level ) return true;
>     if( pos < src.pos ) return true;
>     return false;
> }

えと、まずlevelの大小で判定し、決着がつかない(levelが等値)ならposで判断す
りゃいいのですよね。

if ( level < src.level ) return true;
if ( src.level < level ) return false;
return pos < src.pos;

これでいいんちゃう? メンバ変数増えてもこの要領で。

[cppll:12989] Re: std::map のキーにオブジェクトのインスタンスを使用する

[ | / ▼ | ]
Subject:
[cppll:12989] Re: std::map のキーにオブジェクトのインスタンスを使用する
From:
渡辺昭文 <akifu@...>
Date:
Thu, 14 Jun 2007 14:19:33 +0900
Message-Id:
<op.ttwbqve8drt45d@akifu-x24>
In-Reply-To:
12985
References:
12985
初投稿させていただきます。Akifuと申します。

> bool CLevelPos::operator < (const CLevelPos &src ) const {
>     if( level < src.level ) return true;
>     if( pos < src.pos ) return true;
>     return false;
> }
これでは、level > src.levelのとき(明らかにfalseを返すべきときだと思われる)に、
posの大小を見ることになってしまいます。
正しくは、

bool CLevelPos::operator < (const CLevelPos &src ) const {
    if( level < src.level ) return true;

    if( level == src.level ) {
       return pos < src.pos;
    }
    return false;
}
としたいのではないでしょうか?


--
###################
  akifu@...
  Watanabe Akifumi
###################

Navigation

検索

[検索ヘルプ]

Maintener: Tietew <www.tietew.jp>
Powered by Ruby on Rails, Mongrel, PostgreSQL, and Hyper Estraier.
click here