いつも忘れてしまうので、自分用メモ
1.基本形
sample1.c
#inclide <stdio.h>
double Calc(int a, double f)
{
return a * f;
}
int main()
{
// 初期化
double (*pFunc)(int, double) = Calc;
// 実行
double result = (*pFunc)(3, 2.5);
printf("%f\n", result);
}
2. C++ クラス内のメンバ関数を関数ポインタで扱いたい
sample2.cpp
#include <iostream>
class CHoge
{
public:
CHoge() {}
~CHoge() {}
void FuncAll()
{
// 初期化
int (CHoge:: * pFunc1)(int) = &CHoge::func1;
int (CHoge:: * pFunc2)(int) = &CHoge::func2;
int (CHoge:: * pFunc3)(int) = &CHoge::func3;
// 実行
std::cout << (this->*pFunc1)(3) << std::endl;
std::cout << (this->*pFunc2)(1) << std::endl;
std::cout << (this->*pFunc3)(4) << std::endl;
}
private:
int func1(int n) { return n; }
int func2(int n) { return n * 2; }
int func3(int n) { return n * 3; }
};
int main()
{
CHoge hoge;
hoge.FuncAll();
}
3. vector で回してみる
sample3.cpp
#include <iostream>
#include <vector>
class CHoge
{
public:
CHoge() {}
~CHoge() {}
void FuncAll()
{
// 初期化
std::vector<int (CHoge::*)(int)> vecFuncPtr = {
&CHoge::func1,
&CHoge::func2,
&CHoge::func3,
};
// 同じだけど auto の方が楽
// for (int (CHoge::* pFunc)(int) : vecFuncPtr) {
for (auto pFunc : vecFuncPtr) {
std::cout << (this->*pFunc)(3) << std::endl;
}
}
private:
int func1(int n) { return n; }
int func2(int n) { return n * 2; }
int func3(int n) { return n * 3; }
};
int main()
{
CHoge hoge;
hoge.FuncAll();
}
4. std::function, bind を使ってみる
sample4.cpp
#include <iostream>
#include <functional>
class CHoge
{
public:
CHoge() {}
~CHoge() {}
void FuncAll()
{
// 初期化
auto tfunc1 =
std::bind(&CHoge::func1, this, std::placeholders::_1);
auto tfunc2 =
std::bind(&CHoge::func2, this, std::placeholders::_1);
// autoでもいいけど、使わない書き方も覚えておこう
std::function<int(int)> tfunc3 =
std::bind(&CHoge::func3, this, std::placeholders::_1);
// 実行
std::cout << tfunc1(3) << std::endl;
std::cout << tfunc2(1) << std::endl;
std::cout << tfunc3(4) << std::endl;
}
private:
int func1(int n) { return n; }
int func2(int n) { return n * 2; }
int func3(int n) { return n * 3; }
};
int main()
{
CHoge hoge;
hoge.FuncAll();
}
5. 同様に vector で回してみる
sample5.cpp
#include <iostream>
#include <functional>
#include <vector>
class CHoge
{
public:
CHoge() {}
~CHoge() {}
void FuncAll()
{
// 初期化
std::vector< std::function<int(int)> > vecFunction =
{
std::bind(&CHoge::func1, this, std::placeholders::_1),
std::bind(&CHoge::func2, this, std::placeholders::_1),
std::bind(&CHoge::func3, this, std::placeholders::_1),
};
// 実行
// for (std::function<int(int)> tFunc : vecFunction) {
for (auto tFunc : vecFunction) {
std::cout << tFunc(3) << std::endl;
}
}
private:
int func1(int n) { return n; }
int func2(int n) { return n * 2; }
int func3(int n) { return n * 3; }
};
int main()
{
CHoge hoge;
hoge.FuncAll();
}
6. ラムダ式で書いてみる
sample6.cpp
#include <iostream>
#include <functional>
class CHoge
{
public:
CHoge() {}
~CHoge() {}
void FuncAll()
{
// 初期化
auto lamda1 = [this](int i) { return func1(i); };
auto lamda2 = [this](int i) { return func2(i); };
// auto を使わない書き方
std::function<int(int)> lamda3 = [this](int i) { return func3(i); };
// 実行
std::cout << lamda1(3) << std::endl;
std::cout << lamda2(1) << std::endl;
std::cout << lamda3(4) << std::endl;
}
private:
int func1(int n) { return n; }
int func2(int n) { return n * 2; }
int func3(int n) { return n * 3; }
};
int main()
{
CHoge hoge;
hoge.FuncAll();
}
7. ラムダ式で書いて vector
sample7.cpp
#include <iostream>
#include <vector>
#include <functional>
class CHoge
{
public:
CHoge() {}
~CHoge() {}
void FuncAll()
{
// 初期化
std::vector<std::function<int(int)>> vecFunc = {
[this](int i) { return func1(i); },
[this](int i) { return func2(i); },
[this](int i) { return func3(i); },
};
// 実行
for(std::function<int(int)> func : vecFunc) {
std::cout << func(3) << std::endl;
}
}
private:
int func1(int n) { return n; }
int func2(int n) { return n * 2; }
int func3(int n) { return n * 3; }
};
int main()
{
CHoge hoge;
hoge.FuncAll();
}
8.感想
う~ん。どうだろ? 僕は昔の書き方(sample2, sample3)で慣れ親しんだのでつい書いちゃうけど、今風なら sample6, sample7 あたりがすっきりしているかな。
Comments
インスタンスメンバ関数のポインタが必要になるシーンはそうないのではないでしょうか?
もし必要なら単にstaticなメンバ関数のポインタにして引数にそのクラスのポインタをもらうくらいの方がずっと分かりやすいと思いますよ。
逆にラムダ式を使うケースでは、オブジェクトごとメソッドの実行を遅延したいとき、つまりクロージャーとして使う場合になると思います。例えば以下のような形です。
この場合はthisの中身が書き換わったとしても結果を連動させたくないので、結局thisはキャプチャしません。そしてC++のキャプチャ付きラムダ式の実体はメソッドのポインタではなく、自動生成された関数オブジェクトです。
つまりラムダ式自体も自分でそういうクラスを書けば必要ありません(長くなりますが)。仮にメソッドのポインタが必要になってラムダ式が使えなかったとしても、関数オブジェクトで代用できるということです。なので、よほどの設計ミスを突貫工事で直したいとかでない限り、メソッドのポインタを使う機会はまずないように思います。
std::functionは、他と異なり、構築時にメモリ確保が発生するかもしれないことに注意が必要である場合があります。呼び出しコストも他と比べると若干高い場合が多そうです。それとメンバ関数へのポインタについて
というご意見もあるようですが、私自身は稀に使います。「そう多くはない」には大いに賛成しますが「まずない」とは思っていません。(語感の問題かもしれません)
私以外でも
などで利用が確認できます。
※ 私の使い方や上記のリポジトリの事例は「よほどの設計ミスを突貫工事で直したい」なのかもしれません。
参考になれば幸いです。
dameyodamedame さんがどう思うかに関係なく、私の目の前にあります。
ずいぶんと世の中をお知りなんですね。
存じ上げております。
@macchan さん、@Nabetani さん
誰も実在しないとは言ってませんよ。
同じ型の同じシグニチャを持つメソッドをポインタで呼び分けたいなんて、設計ミス以外で起きる気はしません。IDWriteTextLayout2もどう読んでもそう見えますよね。どうしても大きな変更を加えられないケースで、C++0xより前に使ったことは私もあります。手段を知ることは良いことですが、それを使う必要性が出たときに、どう評価する人がいるかは知っておいた方がいいです。特にこれから使おうとする人はね。
なおメソッドポインタの使用については使用に反対の立場ですが、
std::functionについては個人的には使い勝手が良いので、(オーバーヘッドが許容できるなら)逆に使用に賛成です。型のせいで扱いが面倒なラムダ式の共通の箱として収め方が秀逸に感じます。理想論、あるべき論なんていくらでもできます。過去の設計がタコなんて、いくらでも言えます。だから何? やり直しをしているほど、現場に時間はない。
書き直した時、あなたは全責任を負い、全テストをやる責任者になれますか? 下手すりゃ億単位の金がふっとぶんです。あなたにその覚悟がありますか?
@macchan さん
いくらでも言えるとか、誰のせいとかはどうでもいいんです。これからその技術を使おうとする人たちに、どう伝えるかは大事です。パフォーマンスを犠牲に出来ず、限られた工数の中、ある程度の汎用性、制限可能な形を求めて、先のMSのコードはあのような形になったのだと思います。しかし、そのような経緯はユーザーにとっては関係がありません。
「設計ミス」を言い出したのはあなたですよ。
そして、その技術を使おうとする人たちの何人かはC++11よりも古いコードをメンテする仕事につくかもしれません。
Visual Studio Windows MFCのフレームワーク(イベントハンドラ)を見たことがないんですか? あれはファンクタが存在しなかった頃の産物です。
マクロで隠されているとは言え、あなたの言う「設計ミス」があふれていますが、何か問題でも?
で、この記事をどう書き直せば、あなたの満足いく記事になるんですかね?
@macchan さん
私が言ったのは
という話です。例えばこういう伝え方をするのが私は正しいと思っているので、そう書いています。お話噛み合っていますか?
VC++は1.0から使っていますし、4.0以降は業務でもよく使っていましたよ。MFCは設計ミスだらけで、実装の継承問題が一番大きいと思っていますし、一般にもそういう見解が多いという認識です。
思ってもいないことを書けとは言いませんが、個人的にはメソッドのポインタの使用はあまりオススメしないという断り書きが必要かと思います。理想的には特定のシチュエーションを決め、その解決策をいくつかの方法で提示し、その1つとしてメソッドのポインタを記述、ただしオススメしない、くらいの結論がいいと思います。
いきなり「設計ミス」呼ばわりされて良い気になる人がいるんですかね? すぐ話がかみ合うんなら今頃世界は平和になってますね。
MFCのフレームワーク も「設計ミス」と認識しているのなら主張として一貫しています。
ただし、MFCの資産は現実問題としていまだ絶えていません。
しかもMFCを「設計ミス」と表現するは「あなたの意見」に過ぎません。Win32APIをむりやりクラスでラッピングしたらああなるでしょうな。もうちょっとやりようはあったかもしれませんけど。だからC#ができたのでしょう。
え? 最初から書いてますよ。表現が気に入らなかったかもしれませんが。
「おすすめしない」が抜けてるって? 採用する気になれません。たいていの入門書は sample1, sample2 あたりを載せてますし、何よりも「私用のメモ」ですから。
どうしても後世に向けた啓蒙記事を書きたいというならあなたが書けばよろしい。
ついでに書籍化してくださいな。
@macchan さん
設計ミスは設計ミスです。それを提案してる人や積極的に使おうとしてる人や実際に他の選択肢でもいい状況で使ってる人は良い気にならなくていいと思います。ただ話し合いの場を感情論で噛み合わなくする人はそもそも議論に向いていません。
私の主張は少なくともVC++4.0時代には耳にしていましたよ。Win32APIをラップしても普通はああはならないと言われていました。C#は当時飛ぶ鳥落とす勢いだったJavaの対抗だと思うのであまり関係あるとは思いません。
いえいえ、あなたはメソッドのポインタを前提とした話しかしていないので、そうなっていません。
思ってもいないということですよね。使える文法の説明とオススメは別です。そこまで良くないコードをオススメしたいのなら私にはどうすることもできません。
yes。
>そこまで良くないコードをオススメしたい
勝手に解釈しないでいただきたい。
「設計ミス」という強い言葉を使う割には説得力に欠けるんですよね。
世の中には私みたいな「そこまで思ってない人」がたくさんいるんじゃぁないかな。
改宗(?)を迫るのであればそれなりに説得材料(記事)を書いてくださいね。
すでに書いているのならリンクをよろしく。
他にも
などの利用例が見つかります。
なにはともあれ、
という強い思いを抱いている方がいらっしゃるという学びを得ることができました。ありがとうございます。
という記述で、メンバ関数へのポインタを利用しただけで低い評価になってしまう場合があることを知ることができました。ありがとうございます。
とはいえ、私が評価する場合はメンバ関数へのポインタを利用したことを根拠に低い評価をすることはありません。私のような人がいることを知っていおいていただけると良いかもしれません。
※ 脱線しますが、C++ の話をしているときに C++ の用語ではない「メソッド」という言葉を特に注釈や文脈上の必要性を伴わずに使っている場合、ほんの少し評価を下げるかもしれません。このことも知っておいていただけると良いかもしれません。
私としては、メンバ関数へのポインタは
と思っています。プログラミング言語 C++ を見たんですが(極一部しか見てないので見落としがあるかもしれません)、特に使うべきではないという雰囲気は感じませんでした。
「ここはそれを使うべきじゃないよ」と思う利用例に出会うことはありますが、それは他の言語要素と同様です。
参考になれば幸いです。
@macchan さん
時間があったら関連する記事を書くかもしれませんが、私の主張は使うべきでない、というだけなので、数行あれば事足りてしまうのです。単純に言えば、仮想関数やテンプレートを持つ言語で、それを使う理由がないから、というだけです。設計ミスがない限り、使う必要がありません。
@Nabetani さん
さらに例をありがとうございます。実在はするので、探せば出てくることは分かります。まだ中身を見ていませんが、多分どちらも設計ミスですよ。脱線した余談の件ですが、最初はインスタンスメンバ関数のポインタと呼んでいましたが、メソッドポインタの方が短いし、通じるので意図的に変えました(ようはCの関数ポインタと区別が付けばいいので)。伝えている内容に違いはありません。反対意見に対して揚げ足を取りたい気持ちが出てしまうのは分かりますが、感情に流されない方がいいと思いますよ。
また、鍋谷さんがおっしゃるような、忌避すべきという話とはニュアンスが違うのです。単にメソッドポインタが必要になったとしたら、どこかが歪んでいるサインだという話です。本来それは継承なり、テンプレートなり、他の要素で綺麗に分かりやすく解決できたはずだからです。ようはメソッドポインタを使わなければならないほど分解する構造が必要になった=設計ミスだろうと言ってるだけなのですよ。
少なくとも私は「こんな設計ミスなフレームワーク使わせやがって」と思いながらMFCのプログラムを書ける(メンテする)ほどの神経は持ち合わせておりません。そんなの思いながら書いても疲れるだけなので。
ちなみに元記事は関数ポインタも関数オブジェクトも何もオススメしてません。今の流行りとしてファンクタが増えたな、くらいの感覚で、宗教戦争に持ち込むほどの強い意志はありません。
そんなに関数ポインタが嫌いならC++すら捨てる選択肢だって、旦那にはありますぜ。
@macchan さん
Cの関数ポインタはいいのですよ。それは汎用なので必要です。なので、立場上は4.のbindなら影響を局所化出来るので、まだギリギリ許容範囲です。しかし、鍋谷さんに言ったように
ということなので、メソッドポインタが良くないと言っています。
あと言うまでもないですが、特定の言語を好き嫌いでどうこう言うつもりはありません。そもそも選べないことの方が多いし、フレームについても同様です。あと宗教戦争という言葉がお気に入りのようですが、そういう話をしているつもりはないのですよ。私はただ一般論としてメソッドポインタが良くないという話をしているだけで、そもそもそう思ってない方や、伝わらない方は仕方ないと考えている程度です。
以下は「え、メンバ関数へのポインタって使うべきではないの?」というようなことを思ってしまった方々と @macchan(まっちゃん) 様 に向けて書いています。ご了承ください。
パッと例は思いつきませんが、私としては メンバ関数へのポインタを使うのが適切である場面は存在すると思っています。今までも何度も使ってきました。
「メンバ関数へのポインタが使いたくなったから設計ミスだ」と思う必要はありません。
「メンバ関数へのポインタなんてマイナーな機能が本当に必要なの? もうちょっと良い表現、きれいな設計があるかもしれない」ぐらいのことは考えたほうが良いかもしれませんが、検討の結果なお メンバ関数へのポインタが適切だと思うのであれば、おそれずに使うのが良いでしょう。私はそうしていますし、私が例示した通り、多くの C++ ユーザーもそうしていると思います。
メンバ関数へのポインタ は、C++ には古くから存在する言語機能ですが、私の経験の範囲では、特に使うべきではないとか設計ミスのサインだとかいう感じの話は、ここ以外では目にしたことがありません。
多重継承・仮想基底クラスなんかはわりとおすすめしないとよく耳にします(それでも私は使ったことがあります)が、そういう感じではありません。
メンバ関数へのポインタは マイナーな言語機能ですし、仮想関数の場合の挙動がやや難しいということはあります。見慣れない記号の並びになるので C++ の初心者を驚かせてしまうということもあるでしょう。それでも、理解し、おそれず使っていくのが良いと思っています。
参考になれば幸いです。
「単にメソッドポインタが必要になったとしたら、どこかが歪んでいるサインだという話です。本来それは継承なり、テンプレートなり、他の要素で綺麗に分かりやすく解決できたはずだからです。ようはメソッドポインタを使わなければならないほど分解する構造が必要になった=設計ミスだろうと言ってるだけ」
ようやく根拠が出てきたか。遅い。
今回の記事的は(ど〜しても)必要になったので書いただけなんですけどね。
自分自身では、関数ポインタはそれこそ(Windowsの)メッセージループくらいしか使い道がないんじゃぁないかと思ってます。同じパラメータで複数の関数を実行する用途ですからぴったりですよね。
その他の用途には積極的に使いたいとは思わないです。ファンクタも同じ。どうしてこんな面倒くさいものを皆使いたがるのか不思議で仕方ない…というのを書けばいいのかなと思ったり。まぁ付け足しておきます。
例示された内容について
opencvとllvmの例を見ました。完全には理解できていませんが、以下感想です。
(1)opencv編
設計ミスですね。いくつかの操作を組み合わせたものをCNNの中で使いたいだけですが、これ以上クラスを分けるより、グチャッと一つにまとめたかったんでしょう。関数オブジェクトにしようかと思ったんでしょうが、力尽きた印象です。
(2)llvm編
llvmでこのリストがどう使われているのかは見てませんが、何かしら解析した文法構造に対して、直接実行をしたいとかそういうコードなのであればある意味仕方ないのでは?
設計ミスではなく、非常に特殊な用途に見えます。が、これが一般的に有効か?と聞かれたら100%Noだと思うので、ただの例外ですよ。
例えばC++でどうしても静的解析でないリフレクションを使いたい!として実行時にコンパイルすることを前提とした話を、一般的な話に混ぜたら結論が変わってしまうのと同じですよね。
総評
どちらも専門的すぎて素人が数分で判断できる内容ではありません。伝えたいことがあるなら、詳細な説明が必要ですよ。なんでこれを選んだのかは知りませんが、あまりにも投げ捨てな例示になっています。また最初のMSの例でもそうでしたが、明らかに設計ミスな内容をわざわざ選ぶことありません。
例えば…みたいな話
例えばみたいな話だとこんな内容になります。opencvやMSの例に近いですが、犬、猫、鼠にそれぞれN回泣け!みたいな話をクラスの継承とメソッドポインタで実装してみました。
クラスの継承編
メソッドポインタ編
総評
意図が分かりやすいけど、いじりにくいのはクラス継承編の実装で、コンパクトだけど、危なっかしいのはメソッドポインタ編です。意味のないコードなのでどっちがどうという判断はこれ以上言えないのですが、個人的にはクラスの継承編が良くて、メソッドポインタ編が悪いと言っています。
クラスを持たない言語が多い昨今、肌感で嫌だなって思わない人に気持ち悪さを伝えられないのはまあ仕方がないのですよ。
そちらこそ「設計ミス」という言葉がお好きなようで。
これがどれだけ強い言葉なのか心してください。これはマトモに済むはずの議論を感情的に混乱させるものです。dameyoさん自身やその周りではアタリマエかもしれませんけどね。
Cの関数ポインタは良くってC++はダメって、好みの問題でしかない(と思ってます)。せっかくC++を使ってるしstlの恩恵も受けてるんだからそれっぽい書き方しましょうねってだけの、好みの問題でしか無いものを「設計ミス」という強い言葉を使って要らぬ感情を引き起こすことを私は(世界史を皮肉って)「宗教戦争」と表現しています。
時、人、場所によっては喧嘩になりますぜ。
@macchan さん
設計ミスは揶揄などではなく、実際に瑕疵となりうる問題に対して使用する言葉と思っており、そういう意味で使っています。例えばそれが原因で不具合が発生しましたとかそういうことですね。まあRVWで潰せる話でしょうから実際に責任追及するかどうかは別問題ですが。
ただ今回の件は特定の誰かを名指しで責任追及してるわけでもないので、そもそも強い言葉も何もないですよ。お気になさらず。
「肌感で嫌だなって思わない」のは好みとはまた別の話で、経験上複数の要因からなる危険信号というべきモノなのですが、こればかりは状況次第であり、また感覚的な話なので共有する方法がないというか、私の言語化能力を越えています(具体的で共有可能なシチュエーションでないと説明できません)。
「個人的にはクラスの継承編が良くて、メソッドポインタ編が悪いと言っています。」
え? 関数ポインタが嫌いじゃなくって、嫌いなのはそこなの?
mainに直接関数ポインタが書いてあるのはまぁ気持ち悪いし、設計ミスと表現したいのはわかるけど、僕のサンプルコードにはこんなの書いてないよね。
もちろん元記事には継承の話は出てこない。僕がメソッドポインタ編のコードを書いたのならとやかく言われるのはわかるけど、書いてもいないことに対して「設計ミス」と言われる筋合いはないわけで。
解っているとは思うけど、道具に何を使おうがダメな設計は可能です。bind を使おうが、ラムダ式を使おうが、ダメな設計はダメな設計です。
@macchan さん
好き嫌いではありません。何度も書いていて鍋谷さんは理解されているようですが、メソッドポインタをオススメしない理由は、そうなる状況自体が設計ミスであるという点です。
長く携わっている方にすら気持ち悪い点について伝わらないとは思いませんでした。省略した説明を入れておくと、この例で言えば、仮想関数を使えば簡単かつ明快に実現できる内容をわざわざメソッドポインタで行っている点なのです。言い換えれば、本来の綺麗な設計で出来ていないから、メソッドポインタを使わざるを得なくなる、という話なのですよ(この例では簡単に出来ますが、現実のクラスや状況では難しいことが多い)。他の実現手段もありますが、ここでは割愛します。
mainに書くかどうかなんて些細なことは逆に私は気になりません。役割が不明確なクラスに色々入れていないだけです。
根本的なことが伝わっていないようですね。何度か言っているので、言及してる部分を再度読んでほしいところですが、私が設計ミスと言っているのはメソッドポインタを使用せざるを得ない状況のことです。いい加減理解してほしいです。
あなたの記述に矛盾があるからです。
も
も、あなたの大嫌いな「関数ポインタ」ではないのですか? 関数ポインタでなければ何なのですか。
上記の二つはどちらも「設計ミス」なんですよね?
そのうえでこれだ。
もうワケがわからない。いい加減、ご自分の表現の稚拙さに気づいてくださいな。
で、私ははるかに致命的な設計ミスを数万と見てて、「関数ポインタ」なんぞどうでもいい可愛いレベルなんです。関数ポインタはたしかに解決したい問題ではありますが、記憶してる限りでは参照してるインスタンスが行方不明になっているのがほとんどですね。Cの「ポインタを使うときはポインタが指している先のオブジェクト(インスタンス)の生存期間に注意しましょう」程度の域を出なくって、そこまで関数ポインタを悪者にするのは、どうかと思います。
C++になってあまりポインタの先が行方不明になることは少なくなってきたけれども、そういったインスタンスの生存期間を気にしなくても良くなったユーザーが次に陥る罠って大概「マルチスレッドで排他を忘れる」なんですよね(笑)。
本当にダメな設計って、クラスA,Bがたまたま似てるってだけでクラスAだけを使って中でフラグ管理してクラスB機能と切り分けてるとか(ええ、見たことあります)、そういうレベルなんじゃぁないですかね?
@macchan さん
私のこのコメントは元々鍋谷さんに向けたモノです。週明けで忙しくなるだろうし、限られた時間の中で話すのは難しいわけで、返信を受けないように気を遣いながらご自身の意見を語られる鍋谷さんの意図を汲んで、メンションは避けています。ただ記事の著者である @macchan さんがご理解頂けない場合は説明責任があると思い、返事をしている状況です。
にも関わらず、あなたの話はどこまでも実装の話で、設計の話になった途端伝わらなくなり、それを誤魔化すためなのか話の擦り替えを多用されるので散らかっており、閉口しています。
私の話が分からないのであれば、「分からない」と明確にした上で不明点だけ質問するなど、話を続けるなら誤魔化さずに続けるなりの努力をしてもらいたいです。
あと、言葉の定義をどうこう言うのは私の本意ではないのですが、
にある定義のメソッドポインタが設計ミスのサインである、という話を何度もしており、関数ポインタという言葉には「Cの」という枕詞を付けています。いい加減理解してください。
@dameyodamedame 様から思いがけない形で言及されており、たいへん驚いています。
とありますが、誤解です。理解しているような気は全くしません。その後のお話や掲載いただいたコードを読んでいますが(精読はできてません)理解できていないなと思います。
ということで私にも
と思いますが、伝えてほしいと思っているわけでもありませんのでお気になさらぬようお願いします。
せっかくなので追記しますと、メソッドポインタ編 などのソースコード、楽しく拝見しました。拝見して、やはりメンバ関数へのポインタは有用で、使うべき場面があるという自信を持つことができました。ソースコードがあるといいですね。大変参考になりました。ありがとうございます。
なお、掲載いただいたコードがメンバ関数へのポインタの適切な利用方法の例になっていると思っているわけではありません。念の為。
そんな特殊なルールが「伝わる」と思っているのなら、ずいぶんと甘いんじゃぁないですか?
それは伝わらないですよ。元記事も設計の話なんかしていませんし。
ここに「仮想関数」という言葉が出てきています。改めて dameyo さんの投稿を読み返しましたが、あなたの議論は「継承」が前提になっています。元記事は実装の話しかしていないし、設計の話も継承の話もしていません。
「一般的な設計の話」をしたいのかと思いきや、「継承を使った設計の話」なんですね。主語が大きすぎるんです。
関数ポインタの話は未だ釈然としませんが、本質はそこじゃない。
「一般的な実装の話」を「継承を使った設計の話」に勝手にすり替えて「かみ合わない」と言われても困ります。@dameyodamedame さんは反省してください。
そりゃかみ合わないですよね。で、元記事に関してですが「継承を使った設計の話」って元記事に対しては特殊すぎて扱えないのです。
@Nabetani さん
そうはおっしゃいますが、私から見て全く齟齬がないので、明快に伝わっていると思っています。
利用例としてはMSのコードと同じ形なので、例示するまでもないですけどね。私の立場としては使うべきとはならず、よほどの事情がある場合に仕方なく使うという形になると思いますが、意見が違うのは仕方がなく、内容が伝わっているのであれば問題もないのですよ。
@macchan さん
日本語の読解の話なので、伝わらないなら頭悪いだけです。
あなたが気にしているので説明してるだけですよ。私が書いたのは、
ってことだけです。前回も書いていますが、
噛み合っていないのは、あなたが無関係な話にすり替えるばかりで、不明点を明らかにすべく質問をしていないからです。
※なお、一部で特に返事をしていない部分があるのは、ただでさえ散らかりすぎている話がさらに無関係な方向に散らかるために前提部分への言及に留めているからで、返事が出来ないわけではありません。答えることで確実に不明点が減るなら少々遠回りになったとしても回答するので、必要なら再度記載してください。
それでは 2025-02-10 20:25 の質問にお答えください。
(外見は)どうみてもメソッドポインタを使っているのにおススメなさる。その上でメソッドポインタは設計ミスとおっしゃるなら釈明いただきたい。
それでは「世の中にはこれだけ読解力がない人もいるのだ」という学びのつもりでお付き合いください。その辺はお互い様でしょう。
同意します。
ココから誤解がはじまるんです。「設計ミスって何?」ってね。
自分に限って言えば「設計ミスを突貫工事で直したい」場面でなくても
メソッドポインタを使う場面ってあるんじゃぁないかな。
関数オブジェクトだって一種のメソッドポインタでしかない(と勝手に)思っているので、「表記上の違い」にしか見えないんです。だからおススメする/しないにしても使う側の自由と思っています。
私はメソッドポインタを勧める立場はとりません。同様にファンクタを勧める立場もとりません。どちらが良か悪いかは状況次第でしょう。「設計ミス」とは無関係のはずですし、コーデイングルール以上の範疇を出ません。
「メソッドポインタごときで評価うんぬんを言い出す人」がどう評価されるかは知っておいたほうがいいです。
この辺は「そう思う」「そう思わない」の世界でしかないので、
@dameyodamedame さんが力説しても私に届かない(響かない)点かもしれません。
クラスの継承編の
というコードはanimalクラスのポインタに対してbark()メソッドを呼んでいるだけです。
メソッドポインタ編の
というコードはメソッドポインタを使ったanimalsクラスのメソッド呼び出しです。
つまり、実装上の特徴で言えば、一方はクラスポインタで、もう一方はメソッドポインタだということです。
全然理解できません。そりゃかみ合わないわけだ。
それ以前の文脈までは理解できてて結論だけ理解できないということですか?
・「クラスポインタ」と「メソッドポインタ」をわざわざ使い分ける理由がかかりません。
・上の二つのソースにおける見かけ方(使われ方)が理解できません。(少なくとも私にとっては)「どちらも関数へのポインタ」でしかないからでです。
公になっている資料を提示していただけますか? ドマイナーな書籍は困ります。Programing Laguage C++ とか EffectiveC++ 級のベストセラーでお願いします。
どちらか一方の用語を使っている書籍は見たことがありますが、明確にこの二つの用語と用途を使い分けているものを見たことがありません。忘れてたり、見落としていたら教えてください。
単に「ぼくのかんがえたさいきょうのせっけいしそう」に過ぎないのであれば、これを根拠に主張を展開されることはおやめいただきたい。
まずはこれにご回答ください
全く齟齬がないと思われていることに驚いています。偶然そうなっているか、そのように私の文章を誤読しているのかのどちらかです(どちらであるかに興味はありません)。
私自身は「何を言っているのかよくわからない」と思っております。
「伝わっていると思っています」と書かれるたびに「いや伝わってません」と書くのが面倒なので、そういう表明は控えていただければ幸いです。伝わっておりませんし、理解していません。
繰り返しになりますが、伝えてほしいと思っているわけでもありませんのでお気になさらぬようお願いします。
よろしくお願いします。
yes.
この結論に至る根拠が理解できません(当然結論など理解できません)。
理由はこうです。私 wrote:
@Nabetani さん
今まで書かれている内容、自身で挙げられた例示3つについて言及しても何の反応もなく、議論には参加しないつもりに見えるのに、わざわざ伝わってないと言いに来てる点からも、余計に伝わっているのだと思っています。意図に沿えなくてすみませんが…
@macchan さん
が理解できて
が理解できて
が理解できないということであれば、あなたの頭に問題があるという結論になりますね
そこまで意固地になる必要はなくないですか?
なぜそこで明確に区別する必要があるんですか?「メソッドの呼び出し」という言葉まで一緒なのに。
区別する必要もないし、明確に区別している書籍をみたことがないとも言っています。だから公になっていて、明確に区別している書籍があれば紹介してください、ともお願いしています。
根拠となるはずの共通認識を示さずにこのような発言をされることは、同じエンジニアとして残念に思います。まぁ私の頭が悪いんでしょうね。ええ、そういうことにしておいてください。
どう思っても構いませんが、表明しないでいただくと助かります。
コメント欄を読んでいる方々に「わかってるんだ...」という誤解を与えたくないということです。
「伝わっているのだと思っています」の投稿を含めて、本当に何を言っているのかわからないです。ただ、私からの「そういう表明は控えていただければ幸いです。」というお願いが受け入れていただけなかったことだけが伝わりました。
本当に @dameyodamedame の書いていることは私に伝わってませんし、理解してません。
繰り返しになりますが、「伝わっていると思っています」と書かれるたびに「いや伝わってません」と書くのが面倒なので、そういう表明は控えてほしいと強く思っています。
@macchan さん
流石に無理がありすぎますよ。意固地にならないでください。
@Nabetani さん
残念ですが期待に沿えないと書いています。理由も書いたとおりです。議論もしないのに私がどう思っているか表明することに対してわざわざ依頼する必要もないと思いますよ。
の差の説明にしかなってませんね。
「クラスポインタによる呼び出し」
と
「メソッドポインタによる呼び出し」
の違いもまぁわかるような気はしますが。
・実装がメソッドポインタによる呼び出しをせざるを得ない(あるいは強要する)「設計」が存在する(?)
ことがわかっていません。
@dameyodamedame さんが書いたようにその二つは実装側でどうにでも描き分けられる問題と認識しています。(コーディングルールは別として)「メソッドポインタによる呼び出しをせざるを実装せざるを得ない設計(状況)」というのがわかってませんのだけれども。
@macchan さん
つまり、あなたの質問の前半部分、
というところはあなたが完全に間違っていて、今は理解できたということでいいんですよね?
No. 主張されたいことはわかりましたが、あなたの主張を理解するところまでは至っておりません.消化するまで時間ください。
2025-02-11 08:31 の説明を読まなければわかりませんね。
「クラスポインタを使った呼び出しを"クラスポインタ編"とし、
メソッドポインタを使った呼び出しを"メソッドポインタ編"とします。」
という説明が 2025-02-10 04:53 にないから、わからない。
理解できました。間違っていたとは思いません。
前提を書かないから間違える。説明不足ですよ。
お互い様でしょうな。
ダメ出ししても議論が進まないので、次行きましょう。
メソッドポインタは危なっかしいのはわかります。でもそれは C++(C)の「ポインタを使うときは注意しましょう」の域を出ないと思っています。「レガシーな書き方をするな」はわかるのですが、それは設計ミスまで上る話ではないと思っています。
何か説明はありますか?
2025-02-11 12:59 を再掲。
間違いでないとすると、関数ポインタとクラスポインタは同じものなんですね。流石に呆れましたよ。
私の説明は
であり、(N回は普通ないですが)この例は非常に一般的なポリモーフィズムの例ですよ(animal polymorphism c++とかで検索してみて下さい)。誰でもこの手の例示は一度は見てると思い、この例を挙げています。それを敢えてメソッドポインタで実装してみたわけです。
分かりやすく似たような構造に見えるように意図的に実装しているわけですよ。それをクラスポインタと書かないと分からない、前提が必要、とかは流石に受け入れられません。チュートリアルレベルなので。
そして比較できるようにしているのは、このとても綺麗な(設計意図が分かりやすい)クラス設計を、メソッドポインタを使った汚い形でも、簡単に実装できることを示すためです。設計の話が分からない人でも、すぐ理解できるよう簡単なコードで例示したつもりなんですよ。
なので、まさか何十年もC++使ってきた人にここまで噛み砕いた説明がないと伝わらない(正直この説明でも伝わらないのかもと思っていますが…)とは思いませんでした。
それでも分からないケースに備え、念の為にクラス図を用意しました。自動生成したものなので、書き方についての文句はツールの作成者に言って下さい。
クラスの継承編
メソッドポインタ編
では繰り返しますが、前回回答では間違いではないと言っていたこちらのあなたの質問の前半部分は、何の前提も必要なく、あなたが完全に間違っていることを認めますか?
なんか返信したコメントが意味不明に消えて、同じような内容が後ろにあるんだけどw
Qiitaは(まあ他も普通そうだけど)コメントを修正しても順番変わらないので、新しいコメントをして古いコメントをわざわざ消したみたいですねw
認める返信待ってますよw
メソッドポインタとクラスポインタは違うものと認識しています。
そこはさすがにわかります。
最初の投稿には クラス設計の話も継承の話も出てきません。そこにいきなり継承を用いた(?)クラス設計の話を持ち出されたら面くらいますよ。馬鹿の私が悪いんでしょうけれども。
@dameyodamedame の最初の投稿から理解できていないので、馬鹿を相手にしていると思ってもう少しおつきあいください。
最初の投稿では一つのクラスにしか言及していないので、
・複数のクラスの扱いには言及しないでください。
・もちろん継承には触れないでください。
あくまでも対象は元記事とそれに対する第一コメントです。
確認です。ある一つの独立したクラスにおいて、自分自身のメンバ関数を呼ぶのに
・「代用できる」であって「代用しなければならない」ではないですよね?
・メソッドポインタを使うことは(ほとんどの場合)設計ミスをよほどの突貫工事で直したいことの顕れでしょうか?
・設計ミスだとしたらその根拠は?
これがず~っと引っかかっているんです。
記事に対するコメントは記事に即した内容であること、というのはqiitaに限らずオンラインコミュニティを利用する際の大原則だと思うのですよ。相手が何十年のベテランだからと「書かなくてもわかるだろう」と遠い(?)概念を持ち出されることは大変なマナー違反だと考えます。加えて「書かなくても伝わるはずであり、伝わらなければ相手の能力に問題がある」とするのは相手に失礼であり、非常に身勝手な行為だと思いませんか?
Let's comment your feelings that are more than good