2015-12-19 2 views
5

私はthis questionへの答えとしてこのコードを投稿しようとしていました。考え方はをpointeeに委任し、filter関数が値を変更できないようにすることです。g ++とclang ++ - オーバーロードされた変換演算子によって取得されたポインタを削除するあいまいさ

#include <iostream> 
#include <vector> 

template <typename T> 
class my_pointer 
{ 
    T *ptr_; 

public: 
    my_pointer(T *ptr = nullptr) : ptr_(ptr) {} 

    operator T* &()    { return ptr_; } 
    operator T const*() const { return ptr_; } 
}; 

std::vector<my_pointer<int>> filter(std::vector<my_pointer<int>> const& vec) 
{ 
    //*vec.front() = 5; // this is supposed to be an error by requirement 
    return {}; 
} 

int main() 
{ 
    std::vector<my_pointer<int>> vec = {new int(0)}; 
    filter(vec); 
    delete vec.front(); // ambiguity with g++ and clang++ 
} 

のVisual C++ 12と14エラーなしでこれをコンパイルしますが、GCCとあいまいさがあることをClang on Coliru主張。私は彼らにnon-const std::vector::front過負荷を選択してからmy_pointer::operator T* &を選択することを期待していましたが、それはなぜですか?

+0

constオーバーロードの参照を返すようにしてください – bolov

+2

基本的には、コンパイラはまず何を変換するかを決定してから、オーバーロード解決を実行する必要があります。しかし、ここでは 'int *'と 'const int *'が両方ともコンテキストによって許可されているので、最初のステップで失敗します。 –

答えて

8

[expr.delete]/1:

オペランドがタイプ又はクラス型のオブジェクトへのポインタでなければなりません。 クラス型の場合、オペランドは文脈上暗黙的に変換されます(句 [conv])。オブジェクト型へのポインタに変換されます。

[CONV]/5、強調鉱山:

特定の言語構築物は構築物に適切なタイプの指定されたセットのうちの1つ を有する値への変換を必要とします。このような文脈に現れるクラスタイプEの 発現eは、eは暗黙的であるタイプ Tに変換することができる場合だけが文脈暗黙的に指定されたタイプTと にを変換する がよく形成されていると言われていますEは、戻り値タイプがcv Tである非明示的な 変換関数、または への参照を検索して、Tがコンテキストによって許可されるようにします。 正確に のようなものがあります。T。あなたのコードで

、二つのそのようなT S(int *const int *)があります。したがって、あなたが過負荷の状態になってしまう前に、それは不正な形になります。


この領域には、C++ 11とC++ 14の間に変更があります。 C++ 11 [expr.delete]/1-2オペランドに型をオブジェクトへのポインタ、または単一の非明示的な変換関数(12.3.2)を有するクラス型 を持たなければならない

言いますオブジェクトタイプへのポインタ 。 [...]

オペランドはクラス型を持っている場合、オペランドは、上記の変換関数を呼び出すことにより、ポインタ型に変換されます[...]

、読んでいました場合には、どのあなたのコードを許可し、int* &はオブジェクト型へのポインタではなく参照型なので、常にoperator const int*() constを呼び出します。実際には、実装では、変換関数を "オブジェクトへのポインタへの参照"と同様に考慮し、2つ以上の修飾非明示変換関数があるためコードを拒否します。

+0

私はちょうど 'get'関数と[それがうまくいった](http://coliru.stacked-crooked.com/a/67adffa698e65623)を追加しようとしたので、これは暗黙の変換演算子でのみ起こりますか? – LogicStuff

+1

@LogicStuffを修正してください。メンバ関数を呼び出すのは通常のオーバーロードの解決だけで、ポインタが返された場合は 'delete'がうまくいきます。 –

1

delete式は、引数としてキャスト式をとります。これはconstでも構いません。

vec.front()はconstではありませんが、最初にdeleteのポインタに変換する必要があります。したがって、両方の候補者const int*int*が候補です。コンパイラはどちらを選択することはできません。

最も適切なのは、キャストを使用して選択肢を解決することです。たとえば、次のように

delete (int*)vec.front(); 

備考:ルールが異なっているので、あなたが、代わりに、変換のget()機能を使用する場合、それは動作します。オーバーロードされた関数の選択は、戻り値の型ではなく、パラメータとオブジェクトの型に基づいています。ここでは非constはbest viable関数です。vec.front()はconstではありません。

+1

コンパイラは通常の関数呼び出しのためにconst以外のオブジェクトに対して関数の非constバージョンを常に選択するため、これは完全な答えとは思われません。その場合にあいまいさはありません。だから、なぜこの場合にすべきですか? – Kevin

関連する問題