←検索窓の楽しみ方
  ショッピングモール  掲示板ランキング


【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板1こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.3784

constについて
投稿者---kaz(2005/05/25 01:37:35)


iteratorをかえすBtreeをつくってみたのですが、CONSTについての
質問があります。


#include "btree.h"
#include <stdlib.h>

static void show(btree<int>& tree){
    for(btree<int>::iterator i=tree.begin(); i!=tree.end(); ++i)
        cout << *i << " ";
    cout << endl;
    for(btree<int>::iterator i=tree.find(21);i!=tree.end(); ++i)
        cout << *i << " ";
    cout << endl;
}

int main(){
    btree<int> tree;
    for(int i=0; i<30; i++) tree.insert(rand()%100);
    show(tree);
}


このようにiteratorを返すクラスなのですが、
このbtreeクラスを引数としてとるstatic関数showの以下のようにかえると
コンパイルが通らなくなりました。

static void show(btree<int>& tree)

    |         
    |/

static void show(const btree<int>& tree)

次のようなERRORがでました。


$g++ test.cpp
test.cpp: In function `void show(const btree<int>&)':
test.cpp:5: error: passing `const btree<int>' as `this' argument of `
   BtreeIterator<T> btree<T>::begin() [with T = int]' discards qualifiers
test.cpp:5: error: passing `const btree<int>' as `this' argument of `
   BtreeIterator<T> btree<T>::end() [with T = int]' discards qualifiers
test.cpp:8: error: passing `const btree<int>' as `this' argument of `
   BtreeIterator<T> btree<T>::find(const T&) [with T = int]' discards
   qualifiers
test.cpp:8: error: passing `const btree<int>' as `this' argument of `
   BtreeIterator<T> btree<T>::end() [with T = int]' discards qualifiers


調べてみたのですが、わかりません。
なぜ、CONSTをリファレンスにつけることによってこのようなERRORがでるのでしょうか?
考えられる理由にはどのようなものがあるでしょうか? よろしくお願いします。



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:constについて 3785 Blue 2005/05/25 01:55:35
<子記事> Re:constについて 3787 YuO 2005/05/25 15:33:55


No.3785

Re:constについて
投稿者---Blue(2005/05/25 01:55:35)


>   for(btree<int>::iterator i=tree.begin(); i!=tree.end(); ++i)
おそらく、イテレータがconstでないからではないでしょうか。

一例)
void test( const std::vector< int >& vec )
{
    //std::vector< int >::iterator it = vec.begin();  // これはコンパイルエラーが出る
    std::vector< int >::const_iterator it = vec.begin();
}



この投稿にコメントする

削除パスワード

No.3786

Re:constについて
投稿者---kaz(2005/05/25 14:36:31)


Blueさん解答ありがとうございます。iteratorがconstでないといけないということは
そのクラスがconstでないといけないということだと思うのですが、クラスをconstにするにはどう
するべきなのでしょうか?以下にわたしのbtreeクラスとヘッダーをはります。


template <typename T> class btree {

public:
    typedef BtreeIterator<T> iterator;

    // 以下省略

};



template <typename T> class BtreeIterator {
public:
    T& operator*();
    BtreeIterator<T>& operator++();
    bool operator!=(const BtreeIterator<T>& other) const;
    bool operator==(const BtreeIterator<T>& other) const;
    BtreeIterator(){};
    BtreeIterator(btree<T>* tree,pair<Node<T>*,int> p):mTree(tree),mPosition(p){};
private:
    btree<T>* mTree;
    pair<Node<T>*,int> mPosition;
};


iteratorをvectorのもっているようなconst_iteratorのようにするとよいのだと思うのですが、
そのためにはどこをconstにすればよいのでしょうか?

あと以下のような疑問があります。

static void show(const btree<int>& tree){
    // ここではtreeのconstファンクションしか使えないのでしょうか?
}




この投稿にコメントする

削除パスワード

No.3787

Re:constについて
投稿者---YuO(2005/05/25 15:33:55)


test.cpp:5: error: passing `const btree<int>' as `this' argument of `
   BtreeIterator<T> btree<T>::begin() [with T = int]' discards qualifiers
test.cpp:5: error: passing `const btree<int>' as `this' argument of `
   BtreeIterator<T> btree<T>::end() [with T = int]' discards qualifiers
test.cpp:8: error: passing `const btree<int>' as `this' argument of `
   BtreeIterator<T> btree<T>::find(const T&) [with T = int]' discards
   qualifiers
test.cpp:8: error: passing `const btree<int>' as `this' argument of `
   BtreeIterator<T> btree<T>::end() [with T = int]' discards qualifiers


btree<int>::begin(), btree<int>::end(), btree<int>::find(const int&)
に,
constのものが存在しない為にこれらのエラーが起きています。


常套手段としては,それぞれのconstバージョンを定義してやることになります。



この投稿にコメントする

削除パスワード

No.3788

Re:constについて
投稿者---kaz(2005/05/25 19:13:53)


Yuoさんありがとうございます。しかし、constについて少し混乱しています。
わたしのbegin()の中は次のようになっています。


template<typename T>
BtreeIterator<T> btree<T>::begin() {
    Node<T>* firstNode = getSmallestChild(mRoot);
    pair<Node<T>*,int> position = make_pair(firstNode,0); 
    return BtreeIterator<T>(this,position);
}

これを

template<typename T>
BtreeIterator<T> btree<T>::begin() const{
    // 省略
    return BtreeIterator<T>(this,position);
}

のようにconstに変えてやるとコンパイラーの方から


btree.tem: In member function `BtreeIterator<T> btree<T>::begin() const [with T   = int]':
Main.cpp:36:   instantiated from here
btree.tem:127: error: invalid conversion from `const btree<int>* const' to `
   btree<int>*'
btree.tem:127: error:   initializing argument 1 of `
   BtreeIterator<T>::BtreeIterator(btree<T>*, std::pair<Node<T>*, int>) [with T   = int]'

たぶんBtreeIteratorがconstとして認識されなければならないんだと思うのですが、
そのようにするには以下のどれをconstにすればよいのでしょうか?

template <typename T> class BtreeIterator {
public:
    T& operator*();
    BtreeIterator<T>& operator++();
    bool operator!=(const BtreeIterator<T>& other) const;
    bool operator==(const BtreeIterator<T>& other) const;
    BtreeIterator(){};
    BtreeIterator(btree<T>* tree,pair<Node<T>*,int> p):mTree(tree),mPosition(p){};
private:
    btree<T>* mTree;
    pair<Node<T>*,int> mPosition;
};

クラスをconstとさせる方法がわかりません。よろしくお願いします。



この投稿にコメントする

削除パスワード

No.3789

Re:constについて
投稿者---REE(2005/05/25 20:16:36)


イテレータークラスを非constとconstで独立させる必要があります。
つまり、二つイテレータークラスを作ってください。




この投稿にコメントする

削除パスワード

No.3794

Re:constについて
投稿者---kaz(2005/05/25 23:49:02)


>イテレータークラスを非constとconstで独立させる必要があります。
>つまり、二つイテレータークラスを作ってください。
>

REEさんありがとうございます。二ついるとは考えもしませんでした。。。
しかし、やっぱりconstとしての定義つける方法がわかりません。

以下のように先ほどのものともうひとつつくったのですが、


template <typename T> class btree {

public:
    typedef BtreeIterator<T> iterator;
    typedef ConstBtreeIterator<T> const_iterator;

    // 以下省略
};

template <typename T> class ConstBtreeIterator {
public:
    const T& operator*() const;
    const T* operator->() const;
    ConstBtreeIterator<T>& operator++();
    //ConstBtreeIterator<T>& operator=(const ConstBtreeIterator<T>& other);
    bool operator!=(const ConstBtreeIterator<T>& other) const;
    bool operator==(const ConstBtreeIterator<T>& other) const;
    ConstBtreeIterator(){};
    ConstBtreeIterator(btree<T>* tree,pair<Node<T>*,int> p):mTree(tree),mPosition(p){};
private:
    btree<T>* mTree;
    pair<Node<T>*,int> mPosition;
};


// BTREEの実装です。

template<typename T>
BtreeIterator<T> btree<T>::begin(){
    Node<T>* firstNode = getSmallestChild(mRoot);
    pair<Node<T>*,int> position = make_pair(firstNode,0); 
    return BtreeIterator<T>(this,position);
}


template<typename T>
ConstBtreeIterator<T> btree<T>::begin() const{
    Node<T>* firstNode = getSmallestChild(mRoot);
    pair<Node<T>*,int> position = make_pair(firstNode,0); 
    return ConstBtreeIterator<T>(this,position);
}

int main(){
    btree<int>::const_iterator it;
    it = t.begin();


}

$ g++ Main.cpp
Main.cpp: In function `int main()':
Main.cpp:34: error: no match for 'operator=' in 'it = btree<T>::begin() [with T
   = int]()'
btree.h:230: error: candidates are: ConstBtreeIterator<int>&
   ConstBtreeIterator<int>::operator=(const ConstBtreeIterator<int>&)

とこのようなERRORがでたのでoperator=をオーバーロードしなければならないのかと思い

template<typename T>
ConstBtreeIterator<T>& ConstBtreeIterator<T>::operator=(const ConstBtreeIterator<T>& other){
    mPosition = other.mPosition;
    mTree = other.mTree;
    return *this;
}

とやってみたのですが、同じERRORメッセージがでます。
何がいけないのでしょうか?御指摘ください。よろしくお願いします。







この投稿にコメントする

削除パスワード

No.3795

Re:constについて
投稿者---YuO(2005/05/26 00:53:38)


>イテレータークラスを非constとconstで独立させる必要があります。

template <typename T> class ConstBtreeIterator {
( 省略 )
    ConstBtreeIterator(btree<T>* tree,pair<Node<T>*,int> p):mTree(tree),mPosition(p){};
private:
    btree<T>* mTree;
    pair<Node<T>*,int> mPosition;


mTreeはconst btree<T> *型でなければなりません。
そうでないと,mTreeを通してmTreeが指すオブジェクトの非constメンバ関数を呼び出すことができてしまい,
ひいては非constメンバ変数を変更することができてしまいます。

おそらく,Node *もconst Node *になるでしょう。
# 実装がわからないのでなんとも言えませんが。



この投稿にコメントする

削除パスワード

No.3796

Re:constについて
投稿者---kaz(2005/05/26 01:40:37)


>mTreeはconst btree<T> *型でなければなりません。
>そうでないと,mTreeを通してmTreeが指すオブジェクトの非constメンバ関数を呼び出すことができてしまい,
>ひいては非constメンバ変数を変更することができてしまいます。
>
>おそらく,Node *もconst Node *になるでしょう。
># 実装がわからないのでなんとも言えませんが。

解答ありがとうございます。ずっとやっていたのですが、ぜんぜん理解できなくて困っていました。
すごくたくさんの疑問点が自分の中であるのですが、

1.メンバ関数のポインターをconstにしなければならないということというのは理解できるのですが、
なぜコンパイラが

template<typename T>
ConstBtreeIterator<T> btree<T>::begin() const{
    Node<T>* firstNode = getSmallestChild(mRoot);
    pair<Node<T>*,int> position = make_pair(firstNode,0); 
    return ConstBtreeIterator<T>(this,position);
}

このbegin()ファンクションから


const btree<T>* mTree;


こうしなさいということがわかるのでしょうか?わたしはクラスの名前にConstBtreeIterator
としてますが、コンパイラからはこれがconst(中のものに手をつけない)ということが
わかるのでしょうか?



2.

const btree<T>* mTree;としたらこのmTreeからはconstのメンバ関数("int function() const"のように
後ろにconstのついてるもの)しか呼ぶことができなくなるということでしょうか?
それともconstを返すメンバ関数しか呼ぶことができないのでしょうか?


3.constを返す場合どこからがconstにならなければならないのでしょうか?

    Node(unsigned int num);
    ~Node();
    void connectChild(int childNum, Node<T>* child);
    pair<int,bool> insertElem(const T& newElem);
    Node<T>* getChild(int childNum) const;
    Node<T>* getParent() const;
    int findElem(T elem) const;
    bool isLeaf() const;
    bool isFull() const;
    int getElemCount() const;
    T& getElem(int index);

これが一番最下層のNODEクラスなんですが、
この三つすべてがconstを頭に付けなくてはならないということでしょうか?

    const Node<T>* getChild(int childNum) const;
    const Node<T>* getParent() const;
    const T& getElem(int index);



4.前回質問させていただいたこのコンパイラのERRORメッセージのoperator=はどのような
理由からでてきているのでしょうか?

$ g++ Main.cpp
Main.cpp: In function `int main()':
Main.cpp:34: error: no match for 'operator=' in 'it = btree<T>::begin() [with T
   = int]()'
btree.h:230: error: candidates are: ConstBtreeIterator<int>&
   ConstBtreeIterator<int>::operator=(const ConstBtreeIterator<int>&)


みなさん毎回質問に答えていただいてすごく感謝です。ありがとうございます。C++って楽しいですよね。すごくいろんなことが
できて。理解できたらもっと楽しいんだと思います。眠れない日々がつづきますが。。。。



この投稿にコメントする

削除パスワード

No.3797

Re:constについて
投稿者---YuO(2005/05/26 03:32:35)


1.メンバ関数のポインターをconstにしなければならないということというのは理解できるのですが、
なぜコンパイラが

template<typename T>
ConstBtreeIterator<T> btree<T>::begin() const{
    Node<T>* firstNode = getSmallestChild(mRoot);
    pair<Node<T>*,int> position = make_pair(firstNode,0); 
    return ConstBtreeIterator<T>(this,position);
}

このbegin()ファンクションから

const btree<T>* mTree;

こうしなさいということがわかるのでしょうか?



型Tのconstでないメンバ関数の中におけるthisの型はT *です。
それに対して,constメンバ関数の中におけるthisの型はconst T *です。

コンパイラが検出したのはconst btree<int> *をbtree<int> *に変換するための道筋が存在しないことです。


const btree<T>* mTree;としたらこのmTreeからはconstのメンバ関数("int function() const"のように
後ろにconstのついてるもの)しか呼ぶことができなくなるということでしょうか?
それともconstを返すメンバ関数しか呼ぶことができないのでしょうか?



constメンバ関数のみが呼べます。


3.constを返す場合どこからがconstにならなければならないのでしょうか?



必要なものです。

例えば,
    void connectChild(int childNum, Node<T>* child);
    pair<int,bool> insertElem(const T& newElem);

などは,Node,ひいてはBTreeの状態を変更するものですから,constメンバ関数たり得ません。

また,
    Node<T>* getChild(int childNum) const;
    Node<T>* getParent() const;
    int findElem(T elem) const;
    T& getElem(int index);

などは,これらの戻り値を使ってNodeの状態を変更を許すのであれば,非constとconstのメンバ関数のオーバーロードをすることになります。

状態の変更を許さないのであれば,戻り値をconstオブジェクトへのポインタまたは参照にします。


そして,
    bool isLeaf() const;
    bool isFull() const;
    int getElemCount() const;

は,状態を問い合わせるだけの関数なので,constメンバ関数として実装します。


4.前回質問させていただいたこのコンパイラのERRORメッセージのoperator=はどのような
理由からでてきているのでしょうか?



tの型は何ですか?

恐らく,btree<int>だったりするのでしょう。
非constメンバ関数とconstメンバ関数があった場合,
オブジェクトがconstとされていればconstメンバ関数が,そうでなければ非constメンバ関数が呼び出されます。
そのため,t.begin()の戻り値の型がbtree<int>::iteratorになり,btree<int>::const_iteratorに変換できないのだと思います。

ConstBtreeIteratorに,
ConstBtreeIterator& operator= (const BtreeIterator<T> & rhs);

という代入演算子を追加してやるとよいでしょう。
# 他にもコンストラクタや==, !=演算子のオーバーロードも必要。



この投稿にコメントする

削除パスワード

No.3798

Re:constについて
投稿者---kaz(2005/05/26 18:40:20)


Yuoさん本当にありがとうございます。今日はconstを理解するためにconstでいろいろ
といじって遊んでいたのですが、以下のようにするとbegin() constがよばれるのですが、


    const btree<int> tree; // const なので

    /*for(int i=0; i<10; i++){
        tree.insert(i);
    }*/
    
    btree<int>::const_iterator ci = tree.begin(); // begin() constがよばれる

このままでは何もTREEにいれることができないので意味がありません。そこで

    btree<int> tree;     // const ではない
    for(int i=0; i<10; i++){
        tree.insert(i);
    }
    
    btree<int>::const_iterator ci = tree.begin(); // begin()がよばれる

とすると今度はtreeがconstではないのでconstのbegin()がよばれずにERRORとなります。

Main.cpp: In function `int main()':
Main.cpp:13: error: conversion from `BtreeIterator<int>' to non-scalar type `
   ConstBtreeIterator<int>' requested


これを解決するにはどのようにすればいいのかなやんでいたのですが、YUOさんのメッセージの最後の行に
かいてあった

ConstBtreeIterator<T>& operator=(BtreeIterator<T>& other);

これをオーバーライドすればいいと思い、ヘッダーファイルにつけて取り合えずは空実装でやってみたのですが、
このオペレータがよばれてないようです。ERRORメッセージがかわりません。)この方法だと思うのですが。。。
あとこの方法だとしても実装でどうしたらBtreeIteratorからConstBtreeIteratorに変換できるのかが
わかりません。

あまり、昨日からかわっていませんが、状態をのせておきます。
よろしくおねがいします。



template <typename T> class btree {

public:

    typedef BtreeIterator<T> iterator;
    typedef ConstBtreeIterator<T> const_iterator;

    // 省略
};


template <typename T> class ConstBtreeIterator {
public:
    const T& operator*() const;
    const T* operator->() const;
    ConstBtreeIterator<T>& operator++();
    ConstBtreeIterator<T>& operator=(BtreeIterator<T>& other);
    bool operator!=(const ConstBtreeIterator<T>& other) const;
    bool operator==(const ConstBtreeIterator<T>& other) const;
    ConstBtreeIterator(){};
    ConstBtreeIterator(const btree<T>* tree,pair<Node<T>*,int> p):mTree(tree),mPosition(p){};
private:
    const btree<T>* mTree;
    pair<Node<T>*,int> mPosition;
};

template<typename T>
ConstBtreeIterator<T>& ConstBtreeIterator<T>::operator=(BtreeIterator<T>& other){
    // とりあえず空実装
}
    
template<typename T>
BtreeIterator<T> btree<T>::begin(){
    cout << "begin()" << endl;
    Node<T>* firstNode = getSmallestChild(mRoot);
    pair<Node<T>*,int> position = make_pair(firstNode,0); 
    return BtreeIterator<T>(this,position);
}


template<typename T>
ConstBtreeIterator<T> btree<T>::begin() const{
    cout << "begin() const" << endl;
    Node<T>* firstNode = getSmallestChild(mRoot);
    pair<Node<T>*,int> position = make_pair(firstNode,0); 
    return ConstBtreeIterator<T>(this,position);
}



この投稿にコメントする

削除パスワード

No.3799

Re:constについて
投稿者---REE(2005/05/26 19:32:09)


btree<int>::const_iterator ci = tree.begin(); の時は
ConstBtreeIterator<T>::ConstBtreeIterator(const BtreeIterator<T>& other); が呼ばれます。

btree<int>::const_iterator ci;
ci = tree.begin(); の時に
ConstBtreeIterator<T>& operator=(const BtreeIterator<T>& other); が呼ばれます。

上記の二つとも実装しておくのがいいでしょう。



この投稿にコメントする

削除パスワード

No.3800

Re:constについて
投稿者---kaz(2005/05/26 20:35:38)


REEさん、YUOさん、みなさんまじまじ感謝です!!。今日一日朝からずっとconstとつきあってきて
これがわかるまでに(結局REEさんとYUOさんにおしえてもらったんですが。。。)はずかしながら、12時間ぶっ通しでやっとできました。。。
C++は本当になれるまで険しい道のりですね。JAVAのときはここまでわからないことはなかったです。
で、以下のようにやってみました。

    ConstBtreeIterator(const BtreeIterator<T>& other);          
    ConstBtreeIterator<T>& operator=(const BtreeIterator<T>& other);
    bool operator!=(const BtreeIterator<T>& other) const;          

この3つをConstBtreeIteratorクラスにいれました。

BtreeIteratorクラスで

friend class ConstBtreeIterator;

としてConstBtreeIteratorがprivate変数にアクセスできるようにして


template<typename T>
ConstBtreeIterator<T>& ConstBtreeIterator<T>::operator=(const BtreeIterator<T>& other){
    mTree = other.mTree;
    mPosition = other.mPosition;
    return *this;
}

のようにしました。こんな感じでよろしいんでしょうか?
あと、

    btree<int> tree;
    for(int i=0; i<10; i++){
        tree.insert(i);
    }
    for(btree<int>::const_iterator ci=tree.begin(); ci!=tree.end(); ++ci){
        cout << *ci << endl;
    }

このci=tree.begin()に対応するためにoperator!=もオーバーライドしたのですが、
こうするものなのでしょうか?


あと、YUOさんにおしえていただいたように

1 T& getElem(int index);
2 const T& getElem(int index) const;

このように2バージョン用意したのですが、

template<typename T>
const T& ConstBtreeIterator<T>::operator*() const{
    Node<T>* n = mPosition.first;
    int index = mPosition.second;
    return n->getElem(index);             // ここでは1番目のgetElemが選択されている
}

私の実装のどこかがいけないんだと思うんですが、
で、ここで思ったのですが、最下層のgetElemがconstをかえす必要はあるのでしょうか?
このファンクションのリターンはconstになっているのでかえされた時点でconstとなるのではないのでしょうか?

    for(btree<int>::const_iterator ci=tree.begin(); ci!=tree.end(); ++ci){
        *ci = 4;
        cout << *ci << endl;
    }

これを実行したところ

Main.cpp: In function `int main()':
Main.cpp:13: error: assignment of read-only location

このようにちゃんとELEMENTがread-onlyになっていたのですが、どうでしょうか?
間違ったことをいっていたら指摘していただけたらありがたいです。



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧




掲示板提供:Real Integrity