ページ 11

スコープの階層化について

Posted: 2015年1月16日(金) 10:07
by 長峰
C++のスコープについて困っています。

特定のメンバ関数のみで扱う変数があり、それをその関数内で宣言しました。
そのメンバ関数で新たに再帰処理が必要になったので、同じクラスの別メンバとして再帰関数を作りそれを呼び出すようにしたのですが、その関数では変数が参照できませんでした。
現状、引数として渡していますが、何度も呼び出される再帰関数なので非効率的で変だと思っています。

クラススコープの下に2つのメンバ関数で共有する変数をおくスコープがあれば上手くいく感じがしています。
こういった場合、スコープを階層化するにはどうしたらいいでしょうか?

Re: スコープの階層化について

Posted: 2015年1月16日(金) 10:18
by usao
>2つのメンバ関数で共有する変数
メンバ変数ではダメなのでしょうか?

(引数渡しで解決できているのならメンバ増やすよりはそのままの方が良いと思うけど)

Re: スコープの階層化について

Posted: 2015年1月16日(金) 10:41
by みけCAT
例えば、クラスの中にクラスを作ることで擬似的に実現する、とかでしょうか?

コード:

#include <cstdio>

class hoge {
	struct {
		int foo;

		void fuga(void) {
			printf("%d\n", foo);
		}

		void hoge(int a) {
			if (a > 0) foo = a;
			fuga();
		}
	} hogehoge;

	public:
		void test(int param = 123) {
			hogehoge.hoge(param);
		}
};

int main(void) {
	hoge h, hh;
	h.test();
	hh.test(456);
	h.test(-1);
	return 0;
}

Re: スコープの階層化について

Posted: 2015年1月16日(金) 10:42
by 長峰
usaoさん、回答ありがとうございます。
usao さんが書きました:メンバ変数ではダメなのでしょうか?
すごく一時的な変数なので、これを他のメンバ変数と一緒にするのは何か違う気がしています。

Re: スコープの階層化について

Posted: 2015年1月16日(金) 11:04
by 長峰
みけCATさん、サンプルをありがとうございます。
こういった入れ子クラスで、内側のクラスから外側のクラスのメンバ変数にはどうやってアクセスしたらいいでしょうか。

Re: スコープの階層化について

Posted: 2015年1月16日(金) 11:13
by nullptr
スコープを階層化という表現は聞いたことがありませんが。

コード:

#include <functional>
#include <iostream>

struct A{
    int hoge(){
        int foo = 0; // コイツを使いたいのかい?
        
        std::function<int()> rec;
        rec = [&rec,&foo](){
            return foo <= 10 ? foo++ + rec() : 0; // fooが参照で使えるね!やったね!
        };
        return rec();
    }
};

int main(){
    A a;
    std::cout << a.hoge() << std::endl;
}
再帰関数そのものをローカルにしてしまえば簡単。

Re: スコープの階層化について

Posted: 2015年1月16日(金) 11:17
by nullptr
そもそもの話、質問内容の状況があまり考えられないのですが。大抵の場合はその部分を別の部品としてクラス外に吐き出せるのではないでしょうか。

Re: スコープの階層化について

Posted: 2015年1月16日(金) 11:22
by usao
>すごく一時的な変数なので、これを他のメンバ変数と一緒にするのは何か違う気がしています。

一時的な仕事をするだけのクラスか何かを作ってそいつにメンバとして持たせたらいいのでは?
(というか 何がしたいのかわからない)

Re: スコープの階層化について

Posted: 2015年1月16日(金) 11:27
by みけCAT
長峰 さんが書きました:みけCATさん、サンプルをありがとうございます。
こういった入れ子クラスで、内側のクラスから外側のクラスのメンバ変数にはどうやってアクセスしたらいいでしょうか。
参照を渡すことでできました。

コード:

#include <cstdio>

class hoge {
	int bar;

	struct hogehoge_t {
		hoge& parent;
		int foo;

		hogehoge_t(hoge& h): parent(h) {}

		void fuga(void) {
			printf("%d %d\n", foo, parent.bar);
		}

		void poyo(int a) {
			if (a > 0) foo = a;
			fuga();
		}
	} hogehoge;

	public:
		hoge(): hogehoge(*this) {}

		void test(int param = 123) {
			bar = param * 2;
			hogehoge.poyo(param);
		}
};

int main(void) {
	hoge h, hh;
	h.test();
	hh.test(456);
	h.test(-1);
	return 0;
}

Re: スコープの階層化について

Posted: 2015年1月16日(金) 12:03
by 長峰
nullptrさん、回答ありがとうございます。
nullptr さんが書きました:再帰関数そのものをローカルにしてしまえば簡単。
再帰関数をメンバ関数にする必要はなかったので、しっくりくる感じがします。
ただ、無名関数の再帰は初めてなので不安です。関数内で名前付きの関数が宣言できればいいのですが。
nullptr さんが書きました:そもそもの話、質問内容の状況があまり考えられないのですが。大抵の場合はその部分を別の部品としてクラス外に吐き出せるのではないでしょうか。
この関数は全てのメンバ変数を扱うものだったのでクラス内で作ったほうが収まりがいいと思ったのです。
具体的にはこの関数内でオブジェクトの押し出し判定を行い、押せる状況なら、連鎖的にオブジェクトを一方向にずらすというものです。

Re: スコープの階層化について

Posted: 2015年1月16日(金) 12:10
by みけCAT
長峰 さんが書きました:具体的にはこの関数内でオブジェクトの押し出し判定を行い、押せる状況なら、連鎖的にオブジェクトを一方向にずらすというものです。
発想を変えて、文法上の再帰処理を用いずに、スタックを用いた深さ優先探索のような実装ではできないものでしょうか?

Re: スコープの階層化について

Posted: 2015年1月16日(金) 12:19
by nullptr
長峰 さんが書きました:
nullptr さんが書きました:再帰関数そのものをローカルにしてしまえば簡単。
再帰関数をメンバ関数にする必要はなかったので、しっくりくる感じがします。
ただ、無名関数の再帰は初めてなので不安です。関数内で名前付きの関数が宣言できればいいのですが。
無名関数…まあ、無名関数ではありますが、関数オブジェクトとして変数に保存しているので、識別子recをつけていますから、使う分には同じことです。

みけCATさんがクラス内クラスを作っていますが、実は発想は同じものです。関数内で関数は定義できませんが、関数内でローカルクラスを定義して、関数呼び出し演算子をオーバーロードすれば(つまり関数オブジェクトを作れば)同じことができます。

コード:

#include <functional>
#include <iostream>
 
struct A{
    int hoge(){
        int foo = 0; // コイツを使いたいのかい?
        
        struct Rec{
            int& foo;
            Rec( int& foo ): foo{ foo }{}
            int operator()(){
                return this->foo <= 10 ? this->foo++ + this->operator()() : 0;
            }
        } rec{ foo };
        return rec();
    }
};
 
int main(){
    A a;
    std::cout << a.hoge() << std::endl;
}
さっき挙げたコードは、このコードのシンタクスシュガーなのです。関数内でクラス定義すれば関数外からは作れないので、クラス内で定義するより更にスコープを絞れます。

無名関数と怯える必要はありませんので、これを機会に関数オブジェクトを学んでみれば今回の件も幸せになれるかもしれませんね。
具体的にはこの関数内でオブジェクトの押し出し判定を行い、押せる状況なら、連鎖的にオブジェクトを一方向にずらすというものです。
そもそも今回の件なら再帰ではなくてもいいような気もします。
オフトピック
今回の件、最適化の観点からするとローカルクラスで書いた方がいい?まあそもそも再帰しなければ…

Re: スコープの階層化について

Posted: 2015年1月16日(金) 13:12
by 長峰
皆さん返信ありがとうございます。

usaoさん
やっぱり自分のアプローチが適切でなかったようですね。
みけCAT さんが書きました:発想を変えて、文法上の再帰処理を用いずに、スタックを用いた深さ優先探索のような実装ではできないものでしょうか?
深さ優先探索について調べましたがどういった使い方をするのか検討がつきませんでした。
関数から繰り返し構文にして、調べたオブジェクトを配列に入れていくということでしょうか?

nullptrさん
納得のあまり言うことがなくなってしまいましたが、ありがとうございます。
関数オブジェクトは情報だけは知っていたのですが、変更する全てのメンバの参照を持たないといけないと思って避けていました。
考えたら、みけCATさんのコードみたいにクラスオブジェクトの参照だけでいいんですもんね。使い方が分かりました。

Re: スコープの階層化について

Posted: 2015年1月21日(水) 20:06
by 長峰
5日も間隔を空けてしまいましたが、皆さんから見聞きした意見を元に自分の結論をまとめてお開きとさせて頂きます。

・ファイルスコープは名前空間で階層化でき、関数内ではブロックで階層化できるものの、クラススコープは継承による階層化ができるのみで、純粋なサポートはされていない。
・クラス内に作られた入れ子クラスには外側クラスのスコープが適用されるので、外側クラスのインスタンスを通してprivateメンバへアクセスできる。これによって、入れ子クラスで外側クラスの参照を保持すれば擬似的な階層化ができる。

返信をくださった皆さん、遅くなりましたがありがとうございました。
実装自体は再帰関数ではなくループ構文を利用したものに変更し、同じスコープ内の処理にしました。