ExceptionalC++のp,81では「一時オブジェクトを返す場合の型は、
単なるComplexではなく、const Complexとすべきだ。」とあります。
一方、C++ Coding Standardsのp.48-49では戻り値にconstを付ける/付けないの
両論が書かれています。
また、同書C++ Coding Standardsのp.99では
T& operator=(const T&); //古典的
T operator=(T); //最適化される可能性が高い
との記述があります。
C++は進化しているのでしょうか、この2例はどちらが良いのでしょうか、
どなたか、教えてください。
悩むほどの問題ではないのではありませんか。
一時オブジェクトに限らず可能な限りconstオブジェクトを返すよう努力するのは
当たり前に思えます。ただし、long char などの基本型は意味なし。
さて、代入オペレータについては、こんなのやってみればすぐにわかりますよねぇ。
まず、両者は両立しないことを確認してください。
class A{public:
A(){ ::OutputDebugStringA(コンストラクタA()\n);}
A( const A& ex){ ::OutputDebugStringA(コピーコンストラクタA()\n);}
A& operator=(const A& ex){
::OutputDebugStringA(代入演算子(1)\n); return *this;}
A operator=(A ex){
::OutputDebugStringA(代入演算子(2)\n); return *this; }
};
として、(1)(2)を次のコードで
A a1;
A a2;
a1.operator =( a2);
(1)の出力結果は
コンストラクタA()
コンストラクタA()
代入演算子(1)
(2)の出力結果は
コンストラクタA()
コンストラクタA()
コピーコンストラクタA()
代入演算子(2)
コピーコンストラクタA()
となることが確認できます(VS2008SP1 on XP(SP3))。
自分としては現状ユーザーに提供できるもっとも好ましい物を選択
せざるを得ない立場です。
「現在はひどいもんだが5年後に再コンパイルすれば最高さ」
なんて、言えないのです(笑い)。
仲澤@失業者ありがとうございます。
すみません、2番目の問いが間違っていました。
正しくは、
また、同書C++ Coding Standardsのp.99では
T& operator=(const T&); //古典的
T& operator=(T); //最適化される可能性が高い
との記述があります。
です。
よろしくおねがいします。
> T& operator=(T); //最適化される可能性が高い
(2)のケースの出力で、最後のコピーコンストラクタが実行されないだけですよねぇ。
class A、およびそのメンバの全てが比較的単純な継承関係で、かつ、
優秀なコンパイラ、又はオプティマイザを使えば実行されるコピーコンストラクタ
のいくつかが不要であることは自動判別できるでしょう。
a1、a2のどちらかのコンストラクタの実行が不要なのも見抜けるかもしれません。
しかし、ちょっと考えてみれば、
1.そもそもコピーコンストラクタと、代入オペレータが必要になった理由は、
「単純なコピーではすまない処理が必要になった」から。
なのであって、
2.その処理を書くときは、当然十分高速に実行できるようにコードするはず。
だと予測できます。
実際のところ、引数上で意図しないコンストラクターを実行してしまうのは、
明らかなコードミスなのであって、それを数年後のコンパイラがフォロー
してくれのを自分は期待しません。そのために自分は通常のクラスにおいては、
コピーコンストラクタとコピーオペレータは明示的にprivateに宣言します。
この場合本件の様なコードは全てエラーとなるため、すぐに問題点が明らかに
なり、それを実装するか、代替メンバ関数を実装するかを選択することに
なるわけです。
実際、コピーコンストラクタを自動で生成してくれるのは便利な機能であるとは思いま
す
T& operator=(T);
での場合コンパイラがコピーコンストラクタを生成して内容を上手にコピーしてくれま
す
T& operator=(const T&);
での場合はコンパイラがコピーコンストラクタを生成しないと思います
ただし、参照なので、かつ、変更不可なのでコピーコンストラクタで生成された一時オ
ブジェクトと同等に使えると思います
ですので、正直いうと(ぶっちゃけ)どちらでも構わないと思います
ただし,最初のパターンの場合はデストラクタも呼ばれます
えっと たとえば、デフォルトコンストラクタで malloc などがあって,デストラクタで
free する場合、コピーコンストラクタが呼ばれたので、デフォルトコンストラクタが呼
ばれないでも、デストラクタは呼ばれるということになるので、
malloc していない領域を free しにいく恐れがありますね
意図しない動きが発生する可能性があるならば、基本的には自動でなんかしてくれる処
理には
依存しないほうが逆に楽かもしれませんね
私的にはこのオペレータの引数は参照型にするべきかと思います
で、const なんですけど、const だったら渡す側と使う側がはっきりして、間違えて代
入したくなっても
コンパイラに怒ってもらえるので、変更予定のないものは const はつけるべきで、
const はあったほうが便利でないかなって思います
そして、普通は一般的には operator=で結ばれた右辺が変更されることはないはずです
が、私は想像付かないですけど右辺を変えたくなる処理が発生する人は const はつけな
いですよね
operator=を私が実装させる場合は、右辺の変更は許さないので、ぜったい const にし
ておきます
hiroccoさん、ありがとうございます。
T& operator=(T);
の場合、この演算子の実装とTのその他の実装が同じファイルにあれば、
コンパイラがコピーコンストラクタを上手に細工する可能性は高いような
気がしました。(こんな理解でいいのでしょうか。)
さて、1番目の問い、「一時オブジェクトを返す場合はconstをつけよう」ですが、
STLをチラ見した感じでは、STLではconstを付けていないようです。
STLはどのような発想なんでしょうか。
>T& operator=(T);
>の場合、この演算子の実装とTのその他の実装が同じファイルにあれば、
>コンパイラがコピーコンストラクタを上手に細工する可能性は高いような
>気がしました。(こんな理解でいいのでしょうか。)
どうでしょうね?
可能性でプログラムはできないので、意図しないデストラクタが呼ばれても問題ないコ
ードを書くか、常に自分の知る範囲に置くために operator= の引数には参照型として書
くのがいいと思います
私はさらに、
T(const T& object)
{
…
}
とコピーコンストラクタまで実装
あ、途中で送信しちゃいました
とコピーコンストラクタまで実装させたりもよくしています
で、const なんですけど、これをつけるのはホトンド自分のためだと思います
コンパイラが頭が悪いと、もっと効率よくできたのに、どれも同じ処理で配置したりと
かしちゃうので、const をつけてこれは変更しませんよってコンパイラに教えてあげる
必要があったんじゃないのかな?
今のコンパイラは頭がよいので、自分で判断できるんだと思います
つまり、もうコンパイラのための機能は果たしてなくて、自分のためということだと思
います
どうかな?推理も入ってます
とりあえず解決とします。
「一時オブジェクトを返す場合はconstをつけない」
ということで進めたいと思います。
理由は、STLがそうなっている、
STLに慣れた人にとって、constがあると不思議に思うかもしれない、
constをつけて利用を制限する理由を詮索されても答えようがない、
というものです。