ページ 1 / 1
オーバーロードについて
Posted: 2007年7月02日(月) 20:30
by ouh
初めまして、ouh と申します。
いつも拝見させて頂いております。
CではなくC++のお話で恐縮ですが、どうか宜しくお願いします。
質問:
以下のように関数を作っておきます。
// 1)
std::string func( const std::string& s ) {
return ( s + "、こっち" );
}
// 2)
void func( char* s ) {
strcat( s, "、やっぱこっち" );
}
そして以下のように呼び出しをします。
char ss[64];
strcpy( ss, "呼ばれるのは" );
std::string str = func( ss );
当然コンパイルエラーです。 char* を引数にとる func() の戻り値の
型は void なのですから。
ですが、2) が存在しなければ上記の呼び出しはエラーではありません。
std::string str = func( ss );
なら 1) が呼ばれ、
func( ss );
なら 2) が呼ばれるようにしたいのですが、なにかいい手はないでしょうか?
これは雑談ですのでお気軽にいろんなお話が聞きたいです。
いい手が無いなら無いで
1) を呼び出すときは
std::string str = func( std::string( ss ) );
とすればいいだけなのですから。本気の本気で困っての質問ではないので
「雑談」にしました。
Re:オーバーロードについて
Posted: 2007年7月02日(月) 22:22
by Justy
う~ん、オーバーロードは引数だけが対象ですから、そのままでは無理ですね。
テンプレートを使って明示的に戻り値を指定する手もありますが、それだと明示的になってしまうので・・・。
で、思いついたのがこれです。
[color=#d0d0ff" face="monospace]
#include <string>
#include <iostream>
#include <cstring>
class func
{
public:
func(char *p)
: str(p) {}
operator std::string () const
{
return std::string(str) + "、こっち";
}
operator char* () const
{
std::strcat(str, "、やっぱこっち");
return str;
}
private:
char *str;
};
int main()
{
char ss[64];
strcpy( ss, "呼ばれるのは" );
std::string s1 = func(ss); // operator std::string ()を呼び出す
std::cout << s1 << std::endl;
(char*)func(ss); // キャストして強制的に operator char*()を呼び出す
std::cout << ss << std::endl;
char *s2 = func(ss); // 明示的に戻り値があってもいい
std::cout << ss <<std::endl;
return 0;
}[/color]
関数ではなくクラスになってしまっていますが、これなら戻り値に従って
処理関数を変えることが出来ます。
ただ、変換関数の都合上 voidを戻り値とすることはできないので、
(std::stringを戻り値とするものと対になる)もう片方を便宜的に char*を
戻り値とするようにしてあります。
その為、(char*)func(ss) もしくは static_cast<char*>(func(ss))と
キャストするか、実際に戻り値を受け取る必要がありますが。
妥協点としてはこんな感じですかね。
いかがでしょう。
Re:オーバーロードについて
Posted: 2007年7月03日(火) 00:38
by ouh
Justy様、返信ありがとうございます。
> う~ん、オーバーロードは引数だけが対象ですから、そのままでは無理ですね。
お、お恥ずかしい...
今顔から火が出る思いです。
class と operator を使う方法。私は思いつきもしませんでした。
いかにも C++ という感じで C++ が好きな私にはカッコよくも感じられます。
オーパーロードですらないことがわかった以上、戻り値の型が違う func を
同一ライブラリの中に同居させるわけにはまいりません。
幸い void func( char* ) は最近作ったもので、これを呼び出しているコードはま
だ1つも書いていません(もちろん本物は func なんていう名前じゃないですし、
「、やっぱこっち」を付け足すなどという馬鹿みたいなものじゃありません)。
ひょっとしたら Justy様のコード、パクッてしまうかもしれないです...
Re:オーバーロードについて
Posted: 2007年7月03日(火) 00:59
by YuO
そもそも,その2つの関数は同一の名前空間にないといけないようなものなのですか?
そうであれば名前付けを間違っているのだと思いますし,
そうでないならば名前空間をきっちり分離してやれば良いだけだと思いますが。
Re:オーバーロードについて
Posted: 2007年7月03日(火) 01:41
by Justy
>いかにも C++ という感じで C++ が好きな私にはカッコよくも感じられます
言語的には面白い課題ですが、実際に使うのはあまりお薦めしないです。
この operatorは暗黙の変換を促すようなときに使うもので、
今回のようなケースで使うのはちょっとタブー的な領域に足を突っ込んでいますね。
元の関数の中身を正確に把握していないのでなんとも言えませんが、
多分ちゃんと別の名前を付けた方がいいのかもしれません。
Re:オーバーロードについて
Posted: 2007年7月03日(火) 02:54
by ouh
YuO様、Justy様、返信ありがとうございます。
おっしゃるとおりですね。
本来 std::string のものだけでいいですし、戻り値を受け取るタイプと
渡したものを書き換えてもらうものでは別物だから名前か名前空間を変え
たほうがいいですよね。
もともと純粋に関数にしようとしていたので、char* を返してもらったり
参照を返してもらうというのは考えていませんでした。
私が書く C++ のコードがダメダメなんでしょうけれど、C の文法だけで
書いた関数のほうが C++ のものよりいくらやってもちょっぴり速くて。
それほど速いことが重要なケースで使うわけでもないのになんか気になっ
ちゃって。
それで std::string を扱う関数のほとんどに、いわば C バージョンと
でも言うべきものを追加したかったんです。
直前まで char* で来ているなら C っぽい関数のほうを呼びたい、って
いう感じです。
お二方、間抜けな質問にお付き合いくださってほんとうにありがとうご
ざいます。
Re:オーバーロードについて
Posted: 2007年7月03日(火) 03:18
by Justy
>C++ のものよりいくらやってもちょっぴり速くて
この問題は std::stringを使う限り避けられないとは思いますが、
小手先なので良ければを1点だけ。
もし、戻ってきた std::stringが変更されないのであれば
std::string str = func( ss );
ではなく、
const std::string &rstr = func( ss );
とすると、funcの戻り値である一時変数が拘束されて、rstr変数のスコープまで保持されます。
これなら無駄なオブジェクト丸ごとstrへ代入が1つなくなって少しだけ高速化されます。
>直前まで char* で来ているなら C っぽい関数のほうを呼びたい、って
いう感じです
判らなくはないです(w
でもまぁ、文字列の扱いを統一しておかないとバグの元にもなりますし、
文字列を加工するときは一旦は std::stringにしてしまった方が安全だと思います。
(その後再度 char *に変換するのは有りです)
Re:オーバーロードについて
Posted: 2007年7月04日(水) 00:11
by ouh
丸一日開いてしまいました。Justy様、申し訳ありません。
ありがとうございます。
今日は仕事忙しかったうえに中央線止まってくれちゃいまして。
踏んだり蹴ったりで帰ってきました。
> const std::string &rstr = func( ss );
この方法、私の場合最終的にファイルへの出力になることが多く、
受け取ったあと書き換えないような使い方が多いのでとてもいい
方法かもしれません。
しかも出力するとすれば、
std::ifstream ifs( "file" );
ifs << func( ss ) << ...
という感じになるかと思うので、rstr を作る必要もなくてすごく
よさそうです。