2017-07-16 8 views
4

私は、次のクラスがあります。コンストラクタは、常に代わりに、明示的な変換演算子の使用されている

template <typename T1> 
class Foo { 
public: 
    Foo(T1* obj) : obj(obj) {} 

    template <typename T2> 
    Foo(const Foo<T2>& other) : obj(other.obj) {} 

    template <typename T2> 
    explicit operator Foo<T2>() { 
     return Foo<T2>(static_cast<T2*>(obj)); 
    } 
    T1* obj; 
}; 

二コンストラクタの意図はFoo<Y>からFoo<X>からの暗黙的な変換はからY*への暗黙的な変換のときに限り許可されていることです許可されています。

変換演算子はY*からからの明示的な変換を使用してFoo<Y>Foo<X>からの明示的な変換を可能にするために存在します。

しかし、変換演算子は決して使用されないことに気付きました。コンパイラは、明示的キャストを実行しても、常に2番目のコンストラクタを使用します。これは、基になる型の暗黙の変換が不可能な場合にエラーを引き起こします。

上記のクラスをテストするには、次のコードを使用できます。

class X {}; 
class Y : public X {}; 

int main() { 
    Y obj; 
    Foo<Y> y(&obj); 
    Foo<X> x = y; // implicit cast works as expected. 
    // y = x; // implicit conversion fails (as expected). 
    // y = static_cast<Foo<Y>>(x); // conversion fails because constructor is 
            // called instead of conversion operator. 
} 

コンパイラに明示的な変換に変換演算子を使用させる方法はありますか?

+0

コンストラクタ 'explicit'してください。 – user0042

+0

私は単なる明示的な変換/キャストをポインタに静的なキャストを引き起こすために行かないだろう。 (Foo 'をテンプレート関数に渡すと、間違って' Foo 'を無効な方法で変換してしまいます)。代わりに、私は明らかに静的なfooキャストである関数を持っているので、あなたは危険の点を見ることができ、それが意味するときにのみ起こります。 – Yakk

答えて

3

、あなたが直接(Foo<X>である)xからFoo<Y>を構築しようとしている、そのような状況のために変換コンストラクタがconversion functionに好ましいです。

(強調鉱山)

両方の変換関数と変換コンストラクタは変換関数と コンストラクタの両方 コピー初期における過負荷分解能によって考慮され、いくつかのユーザ定義の変換を実行するために を使用することができる場合 コンストラクタは直接初期化コンテキストであるとみなされます

struct To { 
    To() = default; 
    To(const struct From&) {} // converting constructor 
}; 

struct From { 
    operator To() const {return To();} // conversion function 
}; 

int main() 
{ 
    From f; 
    To t1(f); // direct-initialization: calls the constructor 
// (note, if converting constructor is not available, implicit copy constructor 
// will be selected, and conversion function will be called to prepare its argument) 
    To t2 = f; // copy-initialization: ambiguous 
// (note, if conversion function is from a non-const type, e.g. 
// From::operator To();, it will be selected instead of the ctor in this case) 
    To t3 = static_cast<To>(f); // direct-initialization: calls the constructor 
    const To& r = f; // reference-initialization: ambiguous 
} 

あなたはSFINAEことで、この場合のオーバーロードセットから破棄されるように変換コンストラクタを作ることができます。すなわち、根底にあるポインタの暗黙的な変換が許可されている場合にのみ有効にします。

template <typename T2, typename = std::enable_if_t<std::is_convertible<T2*, T1*>::value>> 
Foo(const Foo<T2>& other) : obj(other.obj) {} 

LIVE

2

静的式eが明示的に使用してタイプTに変換することができます[expr.static.cast/4]

をキャスト標準

から次の引用を見てみましょう宣言T t(e);が整形式である場合にはstatic_cast<T>(e)static_castがあり、一部の発明仮変数の場合はt(8.5)です。このような明示的な変換の効果は、宣言と初期化を実行してから変換の結果として一時変数を使用することと同じです。式eは、初期化でそれが左辺値として使用されている場合にのみglvalueとして使用されます。

これは基本的に、変換演算子よりもコンストラクタが優先されていることを示しています。 static_cast<Foo<Y>>(x);については

+0

オーバーロードの解決に頼る前に暗黙的な変換が使用されているので、C++ 17が言い換えを変えて、変換演算子が代わりに使用されていることに興味があります。 – Rakete1111

+0

なぜコンストラクタにディスパッチするのかという質問は出ません。変換演算子を呼び出す方法を尋ねます。あなたは、呪われていない質問に答えました。 「あなたが望むものは不可能です」と付け加えれば、それは答え(おそらく間違ったものですが、答えです)です。これは高品質のコメントですが、質問に対する答えではありません。 – Yakk

関連する問題