2010-03-02 17 views
3

J'essaie actuellement d'utiliser Howard Hinnant's unique_ptr implementation, et je cours dans une erreur de compilation. Voici quelques exemples de code:L'implémentation unique_ptr de Hinnant ne parvient-elle pas à convertir de manière incorrecte dérivée-base dans ce cas?

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 
} 

L'erreur que je reçois est

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>]' 

Il semble que la ligne incriminée ne devrait pas manquer. Est-ce un bug dans l'implémentation, une limitation de l'implémentation due au manque de fonctionnalités du langage C++ 0x, ou une mauvaise compréhension des règles de unique_ptrs?

(Note, je sais que cela ne fonctionnera pas à l'exécution parce que je me déplace la même chose plus d'une fois, je suis juste essayer de comprendre l'erreur de compilation.)

+1

probablement la partie clé de la description est cette « Cette émulation est destinée pour capturer ** le plus ** du comportement du C++ 0X unique_ptr ... ". Je pense que cela implique que tout le comportement n'est pas là. –

+0

Voici l'article qui a conduit à ce comportement de C++ 03: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2000/n1232.pdf –

Répondre

0

Des recherches plus approfondies m'a conduit à this note, me conduit à croire que ce soit une limitation connue de la mise en œuvre:

3 des tests échouent actuellement pour moi (échec au moment de la compilation, censé compiler, exécuter et passe). Ils sont tous associés au constructeur de conversion spécifié dans [unique.ptr.single.ctor].Lorsque la source et la cible sont de différents types, cette émulation exige que la conversion soit explicite, et refuse de compiler sur implicites conversions:

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 . 
+0

L'écriture de litb est très informative, mais je pense que celui-ci est une réponse plus directe à la question. Les deux devraient être lus par les parties intéressées :-) – SCFrench

1

Pour un exemple similaire, voir ce qui devrait ne pas trop

unique_ptr<Base> testBase = move(testDerived); 

le problème ici est de savoir comment la mise en oeuvre sémantique move: le « constructeur de copie » prend une référence non-const, donc ne pas être en mesure de se lier à des temporaires. Pour toujours « passer » de temporaires, la classe a une fonction de conversion (qui suit sont vraiment juste conceptuelle - ils peuvent être mis en œuvre différemment en détail):

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

Et un constructeur prendra cet objet:

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

Voici un exemple qui échoue pour la même raison:

// 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 
}; 

maintenant, pensez à ce code:

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

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

L'initialisation de la copie sera d'abord convertie en A. Mais ensuite, l'objet temporaire A est essayé d'être à nouveau copié sur l'objet final. Cela nécessitera le chemin en utilisant operator E. Mais c'est une autre conversion définie par l'utilisateur lors de l'initialisation, que la norme interdit:

13.3.3.1/4

Lorsqu'il est invoqué pour la copie du temporaire dans la deuxième étape d'une copie-initialisation de classe, [...] , seules les séquences de conversion standard et les séquences de conversion d'ellipse sont autorisées.

C'est pourquoi votre code échoue.