2013-05-13 1 views
-6

J'ai ce problème de conversion avec ce code en C++ 11 standard:C++ 11: conversion const int * int * * en utilisant unordered_set :: push

#include<unordered_set> 
struct B 
{ 
    int x, y; 
}; 

class A 
{ 
    struct hash 
    { 
     std::size_t operator()(int* const a) const 
     { 
     return std::hash<int>()(*a); 
     } 
    }; 

    struct equal_to 
    { 
     std::size_t operator()(int* const a, int* const b) const 
     { 
     return std::equal_to<int>()(*a, *b); 
     } 
    }; 

    private: 
     std::unordered_set< int*, hash, equal_to > set; 

    public: 
     void push(const B& b) 
     { 
     set.insert(&b.x); 
     } 
}; 

Quelqu'un sait pourquoi est-ce? Je peux résoudre le problème en supprimant le modificateur "const" dans l'argument de "push". Mais je ne le veux pas car l'argument "b" n'est pas modifié.

Edit .: Ma simplification du code a produit une adresse non référencée. J'ai fait une structure B l'enlever.

+0

Ce n'est pas C11. –

+2

Vote pour rouvrir. Le problème est dans 'set.insert (& a)', où 'a' a le type' const int & '. L'adresse de 'a' a le type" pointer to const int ", mais l'objet set cherche un" pointer to (modifiable) int ". Ce genre de confusion const est assez commun et mérite une réponse. –

+2

Non lié à votre question, mais vous stockez l'adresse d'un objet qui peut être un objet temporaire dans votre ensemble. Une fois que le 'a' qui a été passé à la méthode' push' est hors de portée, l'adresse est invalide et peut entraîner une corruption du tas ou des applications plus tard si elle est référencée (par exemple votre méthode 'equal_to'). – pstrjds

Répondre

2

La clé du set est déclarée comme étant un pointeur sur int, un int*. Mais ceci:

void push(const B& b) 
{ 
    set.insert(&b.x); 
} 

passe l'adresse d'une int constante, un int const*, d'où l'erreur du compilateur.

Retrait du const de l'argument résoudrait l'erreur du compilateur, comme cela fait le type de clé un int const*, mais ces deux solutions seraient:

  • permis une autre partie du programme, avec non const l'accès à une instance B qui a été passé à push(), pour changer une valeur de l'une des clés dans l'ensemble et briser l'ensemble invariant:

    A a; 
    
    B b1{17, 22}; 
    B b2{30, 22}; 
    
    a.push(b1); 
    a.push(b2); 
    
    b1.x = 30; // set no longer contains unique keys. 
    
  • introduisons une dépendance de la set sur la durée de vie de l'objet référencé par b:

    A a; 
    a.push({14, 23}); // a now contains a dangling pointer. 
    

La meilleure solution est de stocker un int comme la clé, voir http://ideone.com/KrykZw pour démonstration en ligne (grâce à bitmask pour commentaires).


solutions possibles:

  1. copie Dynamiquement b.x. Ou,
  2. Utilisez int const* comme clé. Ou de préférence (en évitant l'allocation dynamique explicite),
  3. Utilisez int comme la clé, au lieu d'un int* (voir http://ideone.com/KrykZw)
+0

Stocker un 'int *' dans un (hash-) ensemble (ou un ensemble normal d'ailleurs), tout en permettant à l'objet pointé de changer et d'avoir la fonction de hachage calculer le hachage basé sur le point-à-objet, non seulement afficher un comportement indéfini, mais aussi certainement planter dans toute mise en œuvre que je peux penser. Votre 3ème solution est la seule qui a du sens. (Notez que 'b.x' peut changer même si' b' est passé comme const ref dans 'push'.) – bitmask

+0

Merci, 2 option est ma solution préférée parce que je ne veux pas utiliser le constructeur de copie. – xgbuils

+0

@jefebrondem, quel constructeur de copie? – hmjd