2016-02-25 1 views
1

Je travaille dans VS2013 et C++ 11.Comment utiliser en toute sécurité std :: move() sur les données de modèle

Je suis en train d'implémenter une collection personnalisée. Lorsque la collection dépasse la capacité, elle redimensionne son stockage. À ce stade, les données doivent déplacer de l'ancien stockage vers le nouveau stockage. Je voudrais vraiment appliquer la sémantique de déplacement sécurisé sur les éléments de données T. Si un élément de données possède une ressource, la propriété de la ressource doit être récupérée du stockage d'origine et être déplacée vers la nouvelle mémoire. Un cas typique peut être des chaînes ou des pointeurs vers un tableau de données ou d'autres ressources.

J'ai plusieurs types de données qui ont des constructeurs de déplacement explicites et des opérateurs d'affectation de mouvement. Mais je reçois des bogues si ces types avec des constructeurs de mouvement explicites (DeepData1) sont eux-mêmes des membres d'autres structures de données avec des constructeurs triviaux (DeepData2). D'après la façon dont j'ai lu cet article, je pense que je devrais obtenir un constructeur de mouvement généré par le compilateur implicite sur DeepData2. Mais dans l'exemple ci-dessous, je montre que s'appuyer sur les constructeurs implicites de DeepData2 se bloque en raison de la double suppression sur le pointeur _IMPORTANT_DATA. Si je rend le constructeur de déplacement de DeepData2 explicite, le code fonctionne bien.

J'avais espéré ne pas avoir besoin de faire cela et de pouvoir compter sur des constructeurs de mouvement implicites. Sinon, il semble que le code utilisateur doive se souvenir de fournir le constructeur et l'assignation supplémentaires. Si DeepData2 a besoin d'un constructeur de déplacement explicite, puis-je en faire une erreur si le code utilisateur oublie d'en fournir un? ? Est-il possible de détecter si un type de modèle requiert un constructeur de déplacement explicite en raison d'un membre ayant un constructeur de déplacement explicite? Quand j'utilise les caractères de type std, ils ne semblent pas me donner suffisamment d'informations pour écrire un bon argument comme "bon code utilisateur, pour le template T, vous devez déplacer la sémantique et j'ai oublié"

Ceci est compliqué. Merci pour tout conseil ou de l'aide

#include "stdafx.h" 

#include <vector> 
#include <algorithm> 
#include <iostream> 


template <typename T> 
class DeepVector 
{ 
public: 
    DeepVector() 
    { 
     deepCopyResize(4); 
    } 

    void push_back(T& v) 
    { 
     if (_capacity <= _count) 
     { 
      deepCopyResize(_capacity * 2); 
     } 

     // !! deep copy desired here !! 
     _data[_count++] = std::move(v); 
    } 

    T& operator[](int i) { return _data[i]; } 

    void deepCopyResize(int cap) 
    { 
     int n = std::min(_count, cap); 
     T* d = new T[cap]; 
     if (_data) 
     { 
      for (int i = 0; i < n; ++i) 
      { 
       // !! deep copy desired here !! 
       d[i] = std::move(_data[i]); 
      } 
      delete[] _data; 
     } 
     _data = d; 
     _capacity = cap; 
     _count = n; 
    } 
private: 

    int _capacity = 0; 
    int _count = 0; 
    T* _data = nullptr; 
}; 


struct FlatData1 
{ 
    int x = 0, y = 0; 
}; 

struct DeepData1 
{ 
    DeepData1() 
    { 

    } 

    DeepData1(int s) 
    { 
     _size = s; 
     _IMPORTANT_DATA = new int[_size]; 
    } 

    // move constructor 
    DeepData1(DeepData1&& rhs) 
    { 
     _size = rhs._size; 
     _IMPORTANT_DATA = rhs._IMPORTANT_DATA; // pilfer 
     rhs._size = 0; 
     rhs._IMPORTANT_DATA = nullptr; 
    } 

    // move operator 
    DeepData1& operator=(DeepData1&& rhs) 
    { 
     _size = rhs._size; 
     _IMPORTANT_DATA = rhs._IMPORTANT_DATA; // pilfer 
     rhs._size = 0; 
     rhs._IMPORTANT_DATA = nullptr; 
     return *this; 
    } 

    ~DeepData1() 
    { 
     if (_IMPORTANT_DATA) 
     { 
      std::cout << "non-trivial destructor" << std::endl; 
      _size = 0; 

      // it is an error to delete important twice 
      delete[] _IMPORTANT_DATA; 
      _IMPORTANT_DATA = NULL; 
     } 
    } 
    int _size = 0; 
    int* _IMPORTANT_DATA = nullptr; 

    void resize(int s) 
    { 
     delete[] _IMPORTANT_DATA; 
     _IMPORTANT_DATA = new int[s]; 
     _size = s; 
    } 
}; 

struct DeepData2 
{ 
    int z = 0; 
    DeepData1 problem;  // this data does not deep copy implicitly ? 

    // DeepData2() {} 

// despite C++ standard forcing default not supported by VS2013 
// DeepData2(DeepData2&&) = default; 


// DeepData2(int s) : problem(s) {} 
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
    // where are my implicit move constructors? 

    // I have to uncomment these for the 
    // DeepData::operator=(DeepData&& rhs) 
    // operator to be called 

    /* 
    // have to manually implement move constructor? 
    DeepData2(DeepData2&& rhs) 
    { 
    z = std::move(rhs.z); 
    problem = std::move(rhs.problem); 
    } 

    // move operator 
    DeepData2& operator=(DeepData2&& rhs) 
    { 
    z = std::move(rhs.z); 
    problem = std::move(rhs.problem); 
    return *this; 
    } 
    */ 

    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
}; 


int _tmain(int argc, _TCHAR* argv[]) 
{ 

    // ---------------------------------------------- 
    DeepVector<int> v1; 
    for (int i=0; i<5; ++i) 
    { 
     v1.push_back(i); 
    } 

    if (v1[4] == 4) 
    { 
     std::cout << "resize 1 worked" << std::endl; 
    } 

    // ---------------------------------------------- 
    DeepVector<FlatData1> v2; 
    for (int i = 0; i < 5; ++i) 
    { 
     v2.push_back(FlatData1()); 
     v2[i].x = i; 
     v2[i].y = i; 
    } 

    if (v2[4].x == 4) 
    { 
     std::cout << "resize 2 worked" << std::endl; 
    } 

    // ---------------------------------------------- 
    DeepVector<DeepData1> v3; 
    for (int i = 0; i < 5; ++i) 
    { 
     v3.push_back(DeepData1(10)); 

    } 

    if (v3[4]._size == 10) 
    { 
     std::cout << "resize 3 worked" << std::endl; 
    } 


    // ---------------------------------------------- 


    bool b1 = std::is_move_constructible<DeepData1>(); 
    bool b2 = std::is_move_assignable<DeepData1>(); 
    bool b3 = std::is_trivially_move_assignable<DeepData1>(); 
    bool b4 = std::is_trivially_move_constructible<DeepData1>(); 

    bool b5 = std::is_move_constructible<DeepData2>(); 
    bool b6 = std::is_move_assignable<DeepData2>(); 

    // VS2013 says DeepData2 is trivially moveable with the implicit constructors 
    bool b7 = std::is_trivially_move_assignable<DeepData2>(); 
    bool b8 = std::is_trivially_move_constructible<DeepData2>(); 

    DeepVector<DeepData2> v4; 
    for (int i = 0; i < 5; ++i) 
    { 
     DeepData2 d2; 
     d2.problem.resize(10); 
     v4.push_back(d2); 
    } 

    if (v4[4].problem._size == 10) 
    { 
     std::cout << "resize 4 worked" << std::endl; 
    } 


    return 0; 
} 
+2

VS2013 n'implémente pas complètement les règles pour les constructeurs de mouvement implicites. Vous devrez les écrire vous-même, ou utiliser un compilateur plus récent. –

Répondre

2

MSVC2013 ne prend pas en charge généré ou =default déplacer les constructeurs (ou opérateurs d'affectation).

MSVC2015 fait. Son principal composant manquant à être un compilateur C++ 11 réel est ce qu'ils appellent l'échec "expression SFINAE".

Travailler dans MSVC2013 et C++ 11 n'est pas possible sans remplacer le compilateur. Vous pouvez programmer dans un hybride de C++ 03 et les parties de C++ 11 qu'il supporte.

+0

ouais VS2013 est certainement un peu inégale dans son soutien de la norme. Merci les gars. – meissnersd