新訂版More Effective C++ 演算子に関しての質問です。

[戻る]
迷ヤーズ 2009/10/08(木) 13:51:55
新訂版More Effective C++
「項目5:ユーザ定義の変換関数に気をつけよう」という部分で、
斯様な解説があるのですが、このまま、コンパイルをしますと、
Rational r(1, 2); この形を受け取るコンストラクタも無いので、
当然のことですが
error C2078: 初期化子の数が多すぎます。
error C2440: '初期化中' : 'int' から 'Rational' に変換できません。となります、
1, 2 を引き受けるコンストラクタとか、operator double() const;の意味が分らないの
ですが。
識者の方へ解説願えませんでしょうか、よろしくお願いします。

class Rational {
public:
    operator double() const;    // Rational をdouble に変換
}; 

この関数は次のような文脈で自動的に呼び出される。

/** More Effective C++ P23 演算子
* 
*/
#include <iostream>
#include "more_effective_cc2.h"

int main() {
    Rational r(1, 2);        // r は1/2 を持つ
    double d = 0.5 * r;        // r をdouble に変換し、
                    // 次の乗算を行う
}

仲澤@失業者 2009/10/08(木) 14:57:14
> operator double() const;の意味が分らないのですが。

Rationalクラスのインスタンスが doubleにキャストされるときに使用される
オペレータ関数です。

>    double d = 0.5 * r;        // r をdouble に変換し、
>                   // 次の乗算を行う

の行で、インスタンス r がdoubleにキャストされるなら
関数 Rational::operator double() const;
の中身が実行されます。そうしてから 0.5 がpushされて
掛け算が実行されて、結果が d に格納されます(多分)。

ちなみに、これらのコードは原文のままでしょうか。
だとしたら自分としては「ずいぶん不親切なサンプルコードですね」
と笑っちゃうかも(^^;)。

迷ヤーズ 2009/10/08(木) 15:32:46
仲澤@失業者 さんお世話になります。
原書ですが、ここから見ることができます。
http://robotics.dei.unipd.it/~filippo/Robocup/guide%202004/vari%20doc/MEffC++.pdf
p26 の当たりの解説が、当該コードにあたると思います。
さて、
>インスタンス r がdoubleにキャストされるなら
とのことですが、もしもこの意図に従うならば
やはり、この書き方でRational r(1, 2); double にキャストするには、コンストラクタで
この仕様を定義すればよいという、ことでしょうか?

迷ヤーズ 2009/10/08(木) 16:30:25
このようなコードを書いてみましたが、どうも期待する動作にはなりません。
// double d = 0.5 * r;
コメントアウトすると斯様なエラーが出ます?
error LNK2019: 未解決の外部シンボル "public: __thiscall Rational::operator
double(void)const " (??BRational@@QBENXZ) が関数 _main で参照されました。
fatal error LNK1120: 外部参照 1 が未解決です。

#ifndef _INCLUDE_MORE_EFFECTIVE_CC2_H_
#define _INCLUDE_MORE_EFFECTIVE_CC2_H_

#ifndef _INCLUDE_MORE_EFFECTIVE_CC2_H_
#define _INCLUDE_MORE_EFFECTIVE_CC2_H_

class Rational {
public:
    Rational();
    double div(int lhs, int rhs);
    Rational(int lhs_, int rhs_){ div( lhs_, rhs_ ); }
    operator double() const;                // Rational をdouble に変換
};

double Rational::div(int lhs_, int rhs_)
{
    double result;
    result = static_cast<double>(lhs_) / static_cast<double>(rhs_);
    return result;
}

#endif // End of _INCLUDE_MORE_EFFECTIVE_CC2_H_

/** More Effective C++ P23 演算子
 *
 */
#include <iostream>
#include "more_effective_cc2.h"

using namespace std;

int main() {
    Rational r(1, 2);            // r は1/2 を持つ
    //double d = 0.5 * r;        // r をdouble に変換し、
                                // 次の乗算を行う
}

maru 2009/10/08(木) 17:08:55
何か根本的な勘違いをしているような気が...

class Rational { // for rational numbers
public:
Rational(int numerator = 0, // converts int to
  int denominator = 1); // Rational
...
};
class Rational は有理数を表します。
コンストラクタには二つのデフォルト引数、つまり分母(denominator)、分子
(numerator)が必要です。
このRationalクラスの内部表現は記載されていませんが、後ろの方で
 cout << r; // should print "1/2"
と記載されていることから除算は実行せず、分母、分子をそれぞれ内部で保存している
ものと考えます。
つまり、
class Rational { // for rational numbers
private:
  int numerator_;
  int denominator_; 
public:
  Rational(int numerator = 0, int denominator = 1)
    : numerator_(numerator), denominator_(denominator)
    {}
...
};
のような感じ。

ここで、double d = 0.5 * r;という演算を実行したい、ということで、Rationalクラス
にoperator double() const;を定義します。
つまり、
class Rational { // for rational numbers
private:
  int numerator_;
  int denominator_; 
public:
  Rational(int numerator = 0, int denominator = 1)
    : numerator_(numerator), denominator_(denominator)
    {}
  operator double() const
  {  return (double(numerator_))/(double(denominator_));  }
};
これで、
int main() {
    Rational r(1, 2);            // r は1/2 を持つ
    double d = 0.5 * r;        // r をdouble に変換し、
                                // 次の乗算を行う
    return 0;
}
はコンパイル/実行できるようになります。

でも、この「項目5:ユーザ定義の変換関数に気をつけよう」って言っているのは
下手にこの明示的変換関数operator double() const;を定義すると
  cout << r; // should print "1/2"
が"1/2"と印字されなくなるよ、っていっているんですよ。

この本より前にC++の基本的な勉強からやったほうがいいような気が...

maru 2009/10/08(木) 18:18:55
なお、コメントアウトとは、
「プログラムのソースコードやソフトウェアの設定ファイルなどを部分的に修正する
時に、消したい内容を実際に消してしまうのではなく、コメント化することで一時的
に機能しないようにすること。」
なので、
> // double d = 0.5 * r;
> コメントアウトすると斯様なエラーが出ます?
> error LNK2019: 未解決の外部シンボル "public: __thiscall Rational::operator
> double(void)const " (??BRational@@QBENXZ) が関数 _main で参照されました。
> fatal error LNK1120: 外部参照 1 が未解決です。
はおかしな表現です。double d = 0.5 * r;がコメントアウトされていないため、
main から変換関数 double(void)const が呼び出された(参照)が、その関数自体が
存在しないため、外部参照が未解決となったのです。
普通は「このようなエラーが出たので一次的にコメントアウトしました。」等と使い
ます。

PATIO 2009/10/09(金) 18:12:47
あと、サンプルコードをよく見るとコードを全て載せているわけでは
ないことがわかるはずです。

途中に、・・・が入っているでしょう?
これはコードの極一部を提示していると言う意味だと思いますよ。
書いていない部分のコードはわかるよねと言う意味で省略していると
思います。ある意味、言われている本はその辺の行間を読める人を
対象にしていると思いますのでその部分が分からないのであれば、
読み解くのは難しいかもしれません。

古いスレッドには追加発言できません。