0

クラスに変換演算子関数テンプレートを書き込もうとしていますが、完全には理解できないコンパイルエラーが発生します。

class ABC { };

class BBC:public ABC { };

template <class T>
class TestPtr
{
    public:
        TestPtr(T* ptr=0)
            : _pointee(ptr)
        {   }

        TestPtr(TestPtr& rhs)
        {
            this->_pointee = rhs._pointee;
            rhs._pointee= 0;
        }

        template <class U> operator TestPtr<U>();

    private:
        T* _pointee;
};

template <class T> template <class U>
TestPtr<T>::operator TestPtr<U>()
{
    return TestPtr<U>(this->_pointee);   // if this line is changed to 
    //TestPtr<U> x(this->_pointee);      // these commented lines, the 
    //return x;                          // compiler is happy
}

void foo (const TestPtr<ABC>& pmp)
{  }

int main() {
    TestPtr<BBC> tbc(new BBC());
    foo(tbc);
}

上記のコードにより、次のエラーが発生します

TestPtr.cpp: In member function ‘TestPtr<T>::operator TestPtr<U>() [with U = ABC, T = BBC]’:
TestPtr.cpp:38:9:   instantiated from here
TestPtr.cpp:28:34: error: no matching function for call to ‘TestPtr<ABC>::TestPtr(TestPtr<ABC>)’
TestPtr.cpp:28:34: note: candidates are:
TestPtr.cpp:13:3: note: TestPtr<T>::TestPtr(TestPtr<T>&) [with T = ABC, TestPtr<T> = TestPtr<ABC>]
TestPtr.cpp:13:3: note:   no known conversion for argument 1 from ‘TestPtr<ABC>’ to ‘TestPtr<ABC>&’
TestPtr.cpp:9:3: note: TestPtr<T>::TestPtr(T*) [with T = ABC]
TestPtr.cpp:9:3: note:   no known conversion for argument 1 from ‘TestPtr<ABC>’ to ‘ABC*’

今私が困惑しているのは、コンパイラがreturnステートメントではTestPtr<ABC>::TestPtr(TestPtr<ABC>)なくpickを試みていることです。TestPtr<ABC>::TestPtr(ABC *)ただし、最初に目的のコンストラクターを使用して変数を作成してから値を返すと、正常に機能します。また、T*コンストラクターを無駄に明示的にしました。

g++とclang++の両方で試しましたが、同様の結果が得られました。誰かがここで何が起こっているのか説明してもらえますか?

4

2 に答える 2

1

問題はコピーコンストラクターにあります。そのパラメーターはTestPtr&(非定数参照)です:

TestPtr(TestPtr& rhs)

非定数参照は右辺値式にバインドできません。 TestPtr<U>(this->_pointee)一時オブジェクトを作成する右辺値式です。このオブジェクトを直接返そうとする場合は、コピーを作成する必要があります。したがって、コンパイラがコピーコンストラクタを呼び出すことができない場合にこのエラーが発生します。

通常、解決策は、コピーコンストラクターにconst参照を取得させることです。ただし、この場合、解決策は少し注意が必要です。あなたは何をするのと同じようなことをしたいと思うでしょうstd::auto_ptr「std::auto_ptrのコピーコンストラクターをどのように実装できますか?」への回答で、その汚いトリックを説明しました。

または、最近のC ++コンパイラを使用していて、右辺値参照をサポートするコンパイラのみをサポートする必要がある場合は、ユーザーが宣言したmoveコンストラクタ(TestPtr(TestPtr&&))を提供し、copyコンストラクタを抑制する(=deleteコンパイラーは、削除されたメンバー関数をサポートするか、プライベートとして宣言し、定義しません)。


また、ユーザーが宣言したコピー代入演算子を指定する必要があることにも注意してください。(または、型をムーブのみにする場合は、ユーザーが宣言したムーブ代入演算子を指定し、暗黙のコピー代入演算子を抑制する必要があります= delete。これも、または宣言して定義せずに使用します。)

于 2012-04-21T03:15:39.460 に答える
0

コピーコンストラクターでは、何らかの理由でソースポインターをゼロにすることを主張します

TestPtr(TestPtr& rhs)
{
  this->_pointee = rhs._pointee;
  rhs._pointee= 0;
}

なぜあなたはこれをやっている?

ゼロ化を主張しているのでrhs、引数をとして宣言することはできませんconst TestPtr& rhs。これにより、Jamesが説明したように、コードが壊れます。

ソースポインタをゼロにする理由はわかりません。ただやる

TestPtr(const TestPtr& rhs) : _pointee(rhs._pointee)
  {}

そしてそれは動作するはずです。

あなたstd::auto_ptrはインスピレーションを得て、そのコピールーチンでそのようなものを見たのではないかと思います。ただし、std::auto_ptrソースポインタではstd::auto_ptr、ポイントするオブジェクトの所有権を取得し、コピー時にその所有権を譲渡するため、ゼロになります。std::auto_ptr所有権管理の必要性は、オブジェクトを指すだけでなく、オブジェクトを自動的に破棄しようとするという事実によって決まります。

あなたのポインタは何も破壊しようとはしません。尖ったオブジェクトの所有権を取得する必要はありません。このため、ポインタをコピーするときにソースをゼロにする必要はありません。

于 2012-04-21T03:26:10.510 に答える