2017-10-12 8 views
1

は考えてみましょう:このユーザー定義の変換はなぜ行われませんか?

template<typename T> 
struct Prop 
{ 
    T value; 
    operator T() { return value; } 
}; 

int main() 
{ 
    Prop<float> p1 { 5 }; 
    Prop<std::vector<float>> p2 { { 1, 2, 3 } }; 

    float f1 = p1;    // Works fine 
    float f2_1_1 = p2.value[0]; // Works fine 
    float f2_1_2 = p2[0];  // Doesn't compile 

    return 0; 
} 

なぜそのようにマークラインがコンパイルされませんか?提供された変換演算子を使用してstd::vector<>に暗黙の変換を実行してはならないので、[]が見つかりますか?

このサイトにはこの質問のバリエーションを尋ねる質問がたくさんありますが、私がここに当てはまると思うものは見つかりませんでした。テンプレートはstd::vectorと関係がありますか?

+2

'p2 [0]'という表現は実際には 'p2.operator [](0)'と同じです(エラーメッセージから推測できるはずです)。また、 'Prop'クラスには' operator [] '関数がありません。 –

+0

問題は、p2 [0]が1つの式とみなされることです。 p2を独自の式と見なすと、std :: vectorへの変換が実行され、.operator [](0)が呼び出されます。私の実際のコードでPropに追加のメンバーがあるので、PropがそのタイプのTの値の透明なコンテナとして、いくつかの属性とともに動作するようにするための表記上の便利な方法があります。 – Roel

+0

そのようなデータをラップする通常の方法は、メンバーアクセス演算子 ' - >'または参照解除演算子 '*'をオーバーロードすることです。それで 'p2-> at(0)'や '(* p2)[0]'のようなことができます。より扱いにくいですが、 'static_cast'よりも優れています。 –

答えて

4

暗黙的な変換は、添字演算子オーバーロードを含む、メンバ関数呼び出しの目的のために考慮されていません。

宣言されていないメンバ関数がこのように呼び出されるたびに、コンパイラはオブジェクトを変換できるすべての型を見つけ出す必要があります。コンバーターをコンバーターに変換する)、欠落しているメンバー関数が宣言されているかどうかを確認します。どのようにコードの読者のために混乱することは言うまでもありません(変換は変換演算子のケースでは明白かもしれませんが、変換コンストラクタの場合はそうではありません。

だから私はあなたがパスを渡したいベクトルのメンバ関数のそれぞれのメンバ関数を定義する必要があります

を望むように動作するようにプロップを取得するための記述がより便利な方法があります透過的に透過する。

auto operator[](std::size_t pos) { 
    return value[pos]; 
} 
auto operator[](std::size_t pos) const { 
    return value[pos]; 
} 

ラップされたメンバー関数はすべて明示的に宣言する必要があります。別の問題は、型がTに依存する引数です。たとえば、vector::operator[]vector::size_typeを使用しますが、使用する可能性があるすべてのTには定義されていない可能性があります(確かにfloatでは使用できません)。ここでは妥協を図り、std::size_tを使用します。

このような「トランスペアレント」ラッパーを作成するための面倒な方法は、継承です。公開されている継承テンプレートは、自動的に親のすべてのメンバー関数を持ち、暗黙的にそれに変換可能であり、親タイプのポインタと参照で参照することができます。しかし、そのようなアプローチの透明性は、主に~vectorが仮想ではないため、少し問題があります。

template<typename T> 
struct Prop : private T 
{ 
    using T::operator[]; 
    using T::T; 
}; 

注意継承のアプローチはTとしての基本的なタイプを使用してからあなたを防ぐこと:

プライベート継承は、あなたのメンバーのアプローチとしてではなく、非常に良く、構文と同じラッピングすることができます。また、暗黙的な変換も(変換演算子を使用しても)不可能になりますので、の場合、Tを期待するフリー関数でPropTとして使用することはできません。


PS。変換演算子は値を返すので、ベクトルをコピーする必要があります。これは望ましくないことがあります。

+0

それは感謝します、ありがとう。 – Roel

+0

非公開継承で、 'using'を使ってインターフェースの望ましい部分を公開することは、継承の問題の解決策になります。 – Angew

+0

"このアプローチの問題はもちろん、ラップされた関数を提供するものだけをラップすることはできません。まあまあ。ラッパーはテンプレートなので、呼び出さない限り関数はインスタンス化されません。したがって、 '[]'をサポートしない型をラップすることはできますが、そのような場合には '[]'をラッパーに適用することはできません。 – Angew

1

p2.size(); 
p2.begin(); 
p2.push_back(24); 
// etc. 

のいずれかが「

p2.operator[](0); 

doesnのと同等であるにも

p2[0]; 

コンパイルしても意味がないということと同じ方法意味をなさないコンパイルする\


Prop<T>borrowTのメンバーについては、BjarneのC++プロポーザルでは、ドット演算子を言語に追加しています。私はオペレータ->がスマートポインタのために働くのと同じように動作します。 AFAIRには多くの論争があり、私はそれを息を止めませんでした。

A bit of background for the operator dot proposal—Bjarne Stroustrup

Operator Dot (R3) - Bjarne Stroustrup, Gabriel Dos Rei

Smart References through Delegation: An Alternative to N4477's Operator Dot - Hubert Tong, Faisal Vali

Alternatives to operator dot - Bjarne Stroustrup

関連する問題