web-dev-qa-db-ja.com

C ++では変換演算子はどのように機能しますか?

この簡単な例を考えてみましょう:

template <class Type>
class smartref {
public:
    smartref() : data(new Type) { }
    operator Type&(){ return *data; }
private:
    Type* data;
};

class person {
public:
    void think() { std::cout << "I am thinking"; }
};

int main() {
    smartref<person> p;
    p.think(); // why does not the compiler try substituting Type&?
}

C++では変換演算子はどのように機能しますか? (つまり)コンパイラはいつ変換演算子の後に定義された型を置き換えようとしますか?

40
AraK

以下は、変換関数が使用される場合と使用されない場合のランダムな状況です。

まず、同じクラス型または基本クラス型に変換するために変換関数が使用されることはありません。

引数渡し中の変換

引数の受け渡し中の変換では、コピーの初期化の規則が使用されます。これらのルールは、参照に変換するかどうかに関係なく、変換関数のみを考慮します。

struct B { };
struct A {
  operator B() { return B(); }
};
void f(B);
int main() { f(A()); } // called!

引数の受け渡しは、コピー初期化の1つのコンテキストにすぎません。もう1つは、コピー初期化構文を使用する「純粋な」形式です。

B b = A(); // called!

参照への変換

条件演算子では、変換後の型が左辺値の場合、参照型への変換が可能です。

struct B { };
struct A {
  operator B&() { static B b; return b; }
};

int main() { B b; 0 ? b : A(); } // called!

参照への別の変換は、直接参照をバインドするときです

struct B { };
struct A { 
  operator B&() { static B b; return b; }
};

B &b = A(); // called!

関数ポインタへの変換

関数ポインタまたは参照への変換関数があり、呼び出しが行われると、それが使用される場合があります。

typedef void (*fPtr)(int);

void foo(int a);
struct test {
  operator fPtr() { return foo; }
};

int main() {
  test t; t(10); // called!
}

このことは、実際には非常に役立つことがあります。

非クラス型への変換

常にどこでも発生する暗黙の変換では、ユーザー定義の変換も使用できます。ブール値を返す変換関数を定義できます

struct test {
  operator bool() { return true; }
};

int main() {
  test t;
  if(t) { ... }
}

(この場合のboolへの変換は、他の整数型への変換を禁止するために safe-boolイディオム によって安全にすることができます。)変換は、組み込み演算子が特定の型を期待する場所でトリガーされます。 。ただし、変換が邪魔になる場合があります。

struct test {
  void operator[](unsigned int) { }
  operator char *() { static char c; return &c; }
};

int main() {
  test t; t[0]; // ambiguous
}

// (t).operator[] (unsigned int) : member
// operator[](T *, std::ptrdiff_t) : built-in

メンバーの場合、2番目のパラメーターには変換が必要であり、組み込み演算子の場合、最初のパラメーターにはユーザー定義の変換が必要であるため、呼び出しはあいまいになる可能性があります。他の2つのパラメーターはそれぞれ完全に一致します。場合によっては、呼び出しが明確になることもあります(ptrdiff_tintとは異なる必要があります)。

変換関数テンプレート

テンプレートはいくつかの良いことを許可しますが、それらについては非常に注意する必要があります。次の例では、型を任意のポインター型に変換できます(メンバーポインターは「ポインター型」とは見なされません)。

struct test {
  template<typename T>
  operator T*() { return 0; }
};

void *pv = test();
bool *pb = test();

「。」演算子はC++ではオーバーロードできません。また、x.yと言うと、xに対して自動的に変換は行われません。

16
anon

変換は魔法ではありません。 AがBへの変換を持ち、Bがfooメソッドを持っているからといって、a.foo()がB :: foo()を呼び出すという意味ではありません。

コンパイラは4つの状況で変換を使用しようとします

  1. 変数を明示的に別の型にキャストした
  2. 変数を引数として、その位置で異なる型を期待する関数に渡します(ここでは演算子は関数としてカウントされます)
  3. 変数を別のタイプの変数に割り当てます
  4. 変数copy-constructを使用するか、別のタイプの変数を初期化します

継承に関連するもの以外に、3種類の変換があります。

  1. 組み込みの変換(intからdoubleなど)
  2. 暗黙的な構成。クラスBはタイプAの単一の引数を取るコンストラクターを定義し、「明示的な」キーワードでマークしません。
  3. ユーザー定義の変換演算子。クラスAは演算子Bを定義します(例のように)

コンパイラがどのタイプの変換を使用するかを決定する方法と、いつ(特に複数の選択肢がある場合)はかなり複雑で、SOの答えにそれを凝縮しようとするのは悪い仕事です。 C++標準 のセクション12.3は、暗黙の構築とユーザー定義の変換演算子について説明しています。

(私が考えていないいくつかの変換状況やメソッドがあるかもしれないので、何か不足しているのを見かけたらコメントまたは編集してください)

9
Tyler McHenry

暗黙的な変換(変換演算子または非明示的なコンストラクターによる)は、パラメーターを関数(クラスのオーバーロードされた演算子とデフォルトの演算子を含む)に渡すときに発生します。これに加えて、算術型に対していくつかの暗黙の変換が実行されます(charとlongを追加すると、2つのlongが追加され、結果はlongになります)。

暗黙的な変換は、メンバー関数呼び出しが行われるオブジェクトには適用されません。暗黙的な変換のために、「this」は関数パラメーターではありません。

6
Steve Jessop

やったほうがいい

((person)p).think();

コンパイラには、自動的に人にキャストするための情報がないため、明示的にキャストする必要があります。

次のようなものを使用する場合

person pers = p;

次に、コンパイラーは人に暗黙的にキャストするための情報を持っています。

コンストラクタを通じて「キャスト」することができます。

class A
{
public:
   A( int );
};


A a = 10; // Looks like a cast from int to A

これらはいくつかの簡単な例です。キャスト(暗黙的、明示的など)については、説明が必要です。詳細については、本格的なC++の本をご覧ください( this one のような優れたタイトルについては、スタックオーバーフローに関するC++の本に関する質問を参照してください)。

0

//仮想テーブル機能(VFT)

#include <iostream>

using namespace std;

class smartref {
public:
virtual char think() { }//for Late bindig make virtual function if not make virtual function of char think() {} then become early binding and pointer call this class function 
    smartref() : data(new char) { }
  operator char(){ return *data; }
private:
    char* data;
};

class person:public smartref
{
public:
    char think() { std::cout << "I am thinking"; }
};

int main() {
    smartref *p;//make pointer of class
    person o1;//make object of class
    p=&o1;//store object address in pointer
    p->think(); // Late Binding in class person
return 0;
}
0
MAB

Tが必要なタイプUのオブジェクト(参照)を使用しようとすると、コンパイラーは1つ(!)のユーザー定義キャスト(暗黙のctorまたはキャスト演算子)を試みます。

ただし、_._演算子は、常にその左側にあるオブジェクト(参照)のメンバーにアクセスしようとします。それはまさにそれが定義されている方法です。もっと凝ったものが必要な場合は、operator->()をオーバーロードできます。

0
sbi