質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

3718閲覧

テンプレートクラス メンバにアクセスできない

strike1217

総合スコア651

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/01/10 11:33

編集2018/01/10 13:25

リンク内容
前回の問題に類似します。

C++

1template <class Param> 2class func{ 3protected: 4 Param _max; 5 Param* _set; 6 Param _num 7.... 8} 9 10template <class Param> 11class func2 : public func<Param> { // 継承 12public: 13 void test(); 14... 15} 16 17template<class Param> 18void func2<Param>::test(class func<Param> *s1, class func<Param> *s2){ 19 int n = (s1->_max < s2->_num) ? s1->_max : s2->_num; 20 21 for(auto i = 0; i < n; i++) 22 s1->_set[i] = s2->_set[i]; 23 s1->_num = n; 24}

このコードはエラーです。

クラスを関数のパラメータにしています。
s1->_max, s2->_num などの部分ですね。
protected継承を行っているのですが、テンプレートなので、thisを付けなくていけないのですが・・・
リンク内容

今回のような場合、どこにthisを付ければ良いのでしょうか??
this->s1->_max // error!
s1->this->_max // error!

[ error: ‘int func<int>::_max’ is protected within this context ]
[ declared protected here Param _max; ]

privateやprotectedではなく、publicにすれば解決するんですが・・・
アクセス制限の方でちょっとpublicにするのは・・・って感じです。

friendでもできるのですが、継承してprotectedにしても可能なのではないかと思いまして・・・
しかし、テンプレートにするとできません。
protectedの問題を解決できません。
このようなメンバにはどのようにしてアクセスすれば良いのでしょうか??

教えてください。

[追記]
thisを付けると継承したクラス内のメンバ変数にアクセスすることになり、パラメータの方ではない・・・のですね。

すると・・・継承によって他のオブジェクトのアクセス権を獲得するのは無理・・・ということですかね。
friendに頼らないと不可能・・・ぽいかなぁ

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

とりあえず、仮引数内にclassは必要ありません。
あってもいいですが、タイプ数の増加はコンパイルエラーの増加につながりますので、省いてしまうことをお勧めします。

さて、継承を利用して変数へのアクセスをしたいとのことですが、
求めている答えとは遠いかと思いますが、私見を述べさせていただきます。

継承を利用して変数へのアクセスするのはオブジェクト指向のカプセル化の概念に反するものだと思います。
(仕様上どうしても突破したい場合のためのfriendだと考えています。)

テンプレートクラスfuncに対する変更処理はfuncに持たせるべきであり、func2funcに対する特定の操作を纏めたクラスである場合、func2funcが持つ機能を呼ぶにとどめるべきだと考えます。
例えば、こうすればわざわざ親クラスの変数にアクセスせずとも事足りるはずです。

C++

1template <class Param> 2class func{ 3public: 4 void Union(const func<Param> *); 5 func<Param>* difference(const func<Param> *); 6 7 void test(func<Param> *s2){ 8 int n = (_max < s2->_num) ? _max : s2->_num; 9 10 for(auto i = 0; i < n; i++) 11 _set[i] = s2->_set[i]; 12 _num = n; 13 } 14protected: 15 Param _max; 16 Param* _set; 17 Param _num; 18}; 19 20template <class Param> 21class func2{ 22public: 23 void test(func<Param> *s1, func<Param> *s2){ 24 s1->test(s2); 25 } 26 func<Param>* difference(func<Param>* s1, const func<Param>* s2, const func<Param>* s3){ 27 auto ret = s1->difference(s2); 28 ret->Union(s1->difference(s3)); 29 return ret; 30 } 31};

投稿2018/01/10 13:52

moredeep

総合スコア1507

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/01/10 14:06 編集

ふむふむ・・・ なるほど! funcとfunc2の持つ機能を分離しておいたほうが分かりやすいと思ったのですが・・・ 結構難しいですね。 「func2はfuncが持つ機能を呼ぶにとどめるべきだと考えます。」 参考にさせてもらいます。
strike1217

2018/01/11 05:33

friendによる実装はできるだけ避けるべきなんでしょうか??
guest

0

ベストアンサー

こんにちは。

とりあえず、継承したクラス・テンプレートのメンバ変数にアクセスするには、下記記述でできます。

C++

1template<class Param> 2void func2<Param>::test(class func<Param> *s1, class func<Param> *s2) 3{ 4 this->_max = 1; 5 func2<Param>::_num = 1; 6}

しかし、たぶん聞きたいことはこのようなことではないような印象を受けます。
どうも本質的な部分を勘違いされているっぽいです。

継承は、ある意味名前無しのメンバ変数を定義しているのと近いです。

C++

1#include <iostream> 2 3struct Base 4{ 5 int data0; 6}; 7 8struct Derived : public Base 9{ 10 int data1; 11}; 12 13struct Included 14{ 15 Base base; 16 int data1; 17}; 18 19int main() 20{ 21 std::cout << sizeof(Derived) << "\n"; 22 std::cout << sizeof(Included) << "\n"; 23}

上記のDerivedとIncludedの「メモリ・レイアウト」は同じです。data0, data1の順で並んでいます。
IncludedはBaseクラス型のメンバ変数に名前baseがついています。名前がついているので複数のBaseクラス型の変数を定義できます。
DerivedはBaseクラスを含んでいますが、継承であるため名前がありません。名前がないので継承できるものは1つだけです。

というような関係を定義するのが継承です。

friendでもできるのですが、継承してprotectedにしても可能なのではないかと思いまして・・・

friendでできることを継承してやろうとする発想が誤りなのです。
friendは単にアクセス制限を緩めるための仕組みです。継承のようにメモリ・レイアウトを伴う「実体」を定義するものとは概念が全く異なります。


ところで基底クラスがクラス・テンプレートだった場合、いつものようには基底クラスのメンバ変数にアクセスできません。リンク先に書かれているように「this->をつけるかBASE<T>::を付ける必要があるっぽい」です。

クラス・テンプレートは特殊化や部分特殊化できるため、どの定義が選択されるのか実体化されるまで決定できません。そのため、指定したメンバ変数があるかないか分からないのでコンパイルできないということのようです。
this->や基底クラス::は、実体化するまでは「あるかないか判らなくてもよい」ようにする効果があるようです。
詳しくはテンプレートの派生クラスから親クラスのメンバへのアクセスが判りやすいかも。

投稿2018/01/10 13:40

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/01/10 13:47

ふむーー。 継承によってオブジェクトのアクセス権を獲得するのは無理ということですよね? friendを継承するというやり方もあるようです。 http://d.hatena.ne.jp/aki-yam/20100420/1271775548 やはりfriendに頼らないとダメそうですね。
Chironian

2018/01/10 15:32 編集

> friendを継承するというやり方もあるようです。 単にクラスをfiriend指定して、そのクラスのメンバ関数を呼び出してアクセスしているだけです。 至極普通の使い方と思います。 なお、そのクラスを派生したクラスから、他のクラスのprivateメンバへアクセスしていると考えるのは誤りと考えたほうが好ましいと思います。 そのように表現すると、public関数でアクセスしているprivateメンバは、他のクラスからアクセスできるということになります。それはオブジェクト指向プログラミングの隠蔽を破壊する考え方です。 > 継承によってオブジェクトのアクセス権を獲得するのは無理ということですよね? 継承しても基底クラスのprivateメンバにアクセスできるわけではないです。 あっ、基底クラスのprotectedメンバにはアクセスできますね。 Baseクラスを派生したDerivedクラスは、「自分」の中にあるBaseクラスのprotectedメンバにアクセスできます。しかし、他のDerivedクラスのオブジェクトの中にあるBaseクラスのprotectedメンバにアクセスできません。と思ったら出来ました。これは知らなかった。 https://wandbox.org/permlink/pdhQ6AzkxHip4CE5 更にテンプレートにすると https://wandbox.org/permlink/5Tt0oFQUyLObGxUE 他のオブジェクトからアクセスする時は、thisや基底クラス指定はいらないようですね。 先程のリンク先にあるように、thisの場合と同様オブジェクト経由でのアクセスですから、名前解決がテンプレートの実体化より後になるからでしょう。
strike1217

2018/01/11 05:21

クラスの継承か、friendかどちらにしてもアクセス権の獲得はカプセル化を破壊するため良くない・・・ そうすると、chironianさんなら今回のtestメンバ関数のようにクラスを渡して処理するような関数はどのようにして実装しますか??
Chironian

2018/01/11 07:54

privateメンバやprotectedメンバのアクセス権を獲得するという方法は、全てカプセル化を破壊する方法ですね。カプセル化するべき時にまで適用するとアウトです。でも、カプセル化する必要性が低い時に適用してもセーフと私は考えます。リンク先は、一般解として提示してあったのでアウトと感じました。 > chironianさんなら今回のtestメンバ関数のようにクラスを渡して処理するような関数はどのようにして実装しますか?? 例えば、ライブラリの内部からはアクセスできるが、ライブラリの外部からはアクセスできないメンバを定義したいケースがありますね。恐らく希望されている使い方は、このようなレベル分けして、一部のクラスからのみアクセスでき、他のクラスからはアクセスできないようにしたいのだと思います。そのための機能がfriendですね。ライブラリ内部からのアクセス許可であれば、許可したいクラスを全て把握できますから、概ね有用です。 friendで対応できない場合(ライブラリの外部の一部からprivateメンバにアクセスしたいケース)は、カプセル化を断念するかも知れません。設計を見直してそもそも外部からのprivateメンバアクセスを不要とするのが理想ですが、なかなかそうもできない場合がありますし。
strike1217

2018/01/11 07:59

今回の場合は、friendで実装するのが良いということですね。 わかりました。 継承によってアクセス権を獲得するのはやめてfriendにします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問