2017-07-26 3 views
3

Selon std::mapdocumentation, il stocke les paires clé-valeur dans std::pair<const Key, Value>, de sorte que les clés de la carte sont const.Pourquoi est-ce que je peux passer la clé de std :: map à une fonction qui attend non-const?

Maintenant, imaginez que j'ai un std::map où les touches sont les pointeurs vers certains objets.

struct S {}; 
struct Data {}; 
using MyMap = std::map<S*, Data>; 

Supposons aussi qu'il existe une fonction foo qui accepte le paramètre S*.

void foo(S* ptr) { /* modify the object (*ptr) */ } 

Maintenant, la question est: quand j'itérer sur MyMap avec gamme basée à boucle, je suis en mesure de passer l'élément de carte clé foo:

MyMap m = getMyMapSomehow(); 
for (auto elem : m) 
{ 
    static_assert(std::is_const<decltype(elem.first)>::value, "Supposed to be `const S*`"); 
    foo(elem.first); // why does it compile? 
} 

Ainsi, même si mon static_assert réussit (donc je suppose que le type de elem.first est const S*), l'appel à foo se compile bien, et donc il semble que je suis capable de modifier l'objet derrière pointeur-à-const. Pourquoi suis-je capable de faire cela?

P.S. Voici un live example at Coliru qui illustre mon point. Par souci de concision, j'utilise int au lieu de S et Data.

+0

Vous effectuez une copie de l'élément; vous pouvez faire ce que vous voulez avec cette copie personnelle ... –

+2

Ne confondez pas un pointeur vers const avec un pointeur immuable! –

+0

@KerrekSB ah, en effet! Mon cerveau me confond à la fin de ma journée de travail :) Merci! –

Répondre

5

donc je suppose que le type de elem.first est const S*

Non, la clé stockée dans mapconst, std::map<S*, Data>, la clé sera). Donc, il est bon de le passer à foo(S* ptr), le pointeur const lui-même sera copié dans le paramètre.

+0

Vous avez raison, j'ai mélangé 'const T *' avec 'T * const'. De telles choses arrivent parfois à la fin de la journée de travail :-) Merci! –

+0

@VasiliyGalkin: Une remarque: même si 'const X *' et 'const const' sont la même chose, prendre l'habitude d'écrire 'const X 'est utile surtout lorsqu'on travaille avec template et typedefs si on pense * visuellement *. – Nawaz

+0

@Nawaz nah, je wouldn ' t d'accord à cela. Mais je ne voudrais pas commencer encore une autre sainte 'const T *' vs 'T const *'; –

3

Voici un exemple plus simple, voir si vous pouvez travailler dehors:

void f(int);   // takes a non-const int 

int main() { 
    std::set<int> s; // elements are const ints 
    for (auto n : s) { 
     f(n);   // OK!? 
    } 
} 
+0

Ah, en effet! Je n'ai pas fait assez attention au fait que le type de clé est 'T * const' plutôt que' const T * '. Merci! –

+0

'auto n' fait une copie, puis' f (int) 'prend une copie. Donc je n'ai pas compris ce que cette réponse essaie d'expliquer en utilisant des copies! – Nawaz

+0

@Nawaz: c'est fondamentalement la même situation que les faces OP: Une fonction prenant une valeur non const peut être appelée avec une (copie d'un) élément const set. Notez que le code ne fonctionnerait pas si vous aviez 'f (int &)' et 'for (auto & n: s)'.Je comprends qu'il y a un deuxième niveau de confusion, mais je pense que si vous pouvez vous concentrer sur l'exemple simple, vous avez suffisamment d'informations différentielles pour poser la bonne question sur la situation d'origine. –

1

std::map<K, V>::value_type est std::pair<const K, V>, comme vous l'avez mentionné. Alors qu'est-ce que const K quand S* est substitué K? La réponse, qui pourrait vous surprendre, n'est pas const S*. mais plutôt S* const.