3

現在、 Howard Hinnant の unique_ptr 実装を使用しようとしていますが、コンパイル エラーが発生しています。サンプルコードは次のとおりです。

struct Base {};

struct Derived : public Base {};

void testfun(boost::unique_ptr<Base>);

void test()
{
    unique_ptr<Derived> testDerived; 
    unique_ptr<Base> testBase(move(testDerived)); // ok, construct base explicitly from derived 
    testfun(move(testBase));                      // ok, pass base to testfun which expects base 
    testfun(unique_ptr<Base>(move(testDerived))); // ok, explicitly converts to unique_ptr<Base>
    testfun(move(testDerived));                   // error on this line
}

私が得るエラーは

In function 'void test()':
error: no matching function for call to 'boost::unique_ptr<Base, boost::default_delete<Base> >::unique_ptr(boost::unique_ptr<Base, boost::default_delete<Base> >)'
note: candidates are: boost::unique_ptr<T, D>::unique_ptr(boost::detail_unique_ptr::rv<boost::unique_ptr<T, D> >) [with T = Base, D = boost::default_delete<Base>]
note:                 boost::unique_ptr<T, D>::unique_ptr(boost::unique_ptr<T, D>&) [with T = Base, D = boost::default_delete<Base>]
error:   initializing argument 1 of 'void testfun(boost::unique_ptr<Base, boost::default_delete<Base> >)' from result of 'boost::unique_ptr<T, D>::unique_ptr(boost::unique_ptr<U, E>, typename boost::enable_if_c<((((! boost::is_array<U>::value) && boost::detail_unique_ptr::is_convertible<typename boost::unique_ptr<U, boost::default_delete<U> >::pointer,typename boost::detail_unique_ptr::pointer_type<T, D>::type>::value) && boost::detail_unique_ptr::is_convertible<E,D>::value) && ((! boost::is_reference<D>::value) || boost::is_same<D,E>::value)), void>::type*) [with U = Derived, E = boost::default_delete<Derived>, T = Base, D = boost::default_delete<Base>]'

問題のある行は失敗しないようです。これは実装のバグですか、C++0x 言語機能の欠如による実装の制限ですか、それとも unique_ptrs のルールの誤解ですか?

(注: 同じものを複数回移動しているため、実行時にこれが機能しないことはわかっています。コンパイル時のエラーを把握しようとしているだけです。)

4

2 に答える 2

1

同様の例については、失敗するはずの this を参照してください

unique_ptr<Base> testBase = move(testDerived);

ここでの問題は、ムーブ セマンティックの実装方法です。「コピー コンストラクター」は非 const 参照を使用するため、一時オブジェクトにバインドできません。一時的なものから引き続き「移動」するために、クラスには変換関数があります (以下は実際には単なる概念的なものです - 詳細は異なる方法で実装されている可能性があります)。

operator rv<T>() { return rv<T>(*this); }

そして、コンストラクターはそのオブジェクトを受け取ります:

unique_ptr(rv<T> r):ptr_(r.release()) { }

同じ理由で失敗する例を次に示します。

// move helper. rv<> in unique_ptr
struct E { };

// simulates a unique_ptr<D>
struct D { };

// simulates the unique_ptr<B>
struct A {
  A() { }

  // accepts "derived" classes. Note that for unique_ptr, this will need that
  // the argument needs to be copied (we have a by-value parameter). Thus we 
  // automatically ensure only rvalue derived-class pointers are accepted.
  A(D) { } 

  // these will accept rvalues
  A(E) { }
  operator E() { return E(); }

private:
  A(A&); // private, error if passed lvalue
};

ここで、次のコードを検討してください。

// allowed: goes: D -> A(D)
A a((D()));

// compile failure. Goes:
// D -> A(D) -> A(E)
A a = D();

コピーの初期化では、最初に に変換されAます。しかし、その後、一時Aオブジェクトを最終オブジェクトに再度コピーしようとします。これには、 を使用する方法が必要になりますoperator E。しかし、これは初期化における別のユーザー定義の変換であり、標準では禁止されています。

13.3.3.1/4

クラスのコピー初期化の 2 番目のステップ [...] でテンポラリのコピーのために呼び出された場合、標準の変換シーケンスと省略記号の変換シーケンスのみが許可されます。

これが、コードが失敗する理由です。

于 2010-03-02T19:28:42.640 に答える
0

さらに調査した結果、このメモにたどり着き、これが実装の既知の制限であると確信しました。

現在、3 つのテストが失敗しています (コンパイル時に失敗し、コンパイル、実行、および合格するはずです)。これらはすべて、[unique.ptr.single.ctor] で指定された変換コンストラクターに関連付けられています。ソースとターゲットの型が異なる場合、このエミュレーションは変換が明示的であることを要求し、暗黙的な変換でのコンパイルを拒否します。

unique_ptr<base> b(unique_ptr<derived>()); // ok

unique_ptr<base> b = unique_ptr<derived>(); // causes 3 compile time failures under unique.ptr/unique.ptr.single/unique.ptr.single.ctor .
于 2010-03-02T19:30:02.340 に答える