Il me arrive souvent d'avoir un conteneur AA C ++ ne me permet pas de convertir directement tout le conteneur et de forcer un De plus, pour de nombreux cas, je suis sûr que forçant un Y a-t-il un moyen propre et efficace de convertir un C code> (ou quel que soit le type de classe wrapper, même des pointeurs intelligents) pour un type
t1 code> et souhaitez convertir tellement < Code> C
C
t2 code> est compatible avec
t1 code>. p>.
réinterpet_cast code> entraînerait un comportement non défini, alors je devrais créer un nouveau
C
C
T2 code>. Cette opération pourrait être assez chère, à la fois dans le temps et dans l'espace. P>
réinterpret_cast code> fonctionnerait bien avec le code compilé par n'importe quel compilateur jamais existé, par exemple lorsque
t2 code> est
T1 const code> ou lorsque
t1 code> et
t2 code> sont des pointeurs. P>
C
c
Par exemple, un conteneur_cast code> opérateur (/ fonction?) Qui crée et repeupline un
C
C < T1> code>? P>
8 Réponses :
La raison pour laquelle vous ne pouvez pas lancer les conteneurs n'a rien à voir avec les types eux-mêmes. Le problème est que vous essayez de lancer deux objets qui sont, en ce qui concerne le compilateur et la liante, deux classes non liées.
Lorsque vous faites C
et
et
et
et
C
class C_int_ {
//...
};
class C_short_ {
//...
};
Je sais que reterpret_cast code> est dangereux pour cette raison. Avez-vous lu le troisième et quatrième paragraphe sur la question? Quelques types de types sont compatibles binaires i>, je cherche un moyen de laisser le compilateur
reterpret_cast code> dans le cas où ils sont.
Oui je l'ai fait. Mais ce que je dis, c'est que même si les octets sont identiques, ils sont pas i> compatibles binaires. Et, rien que vous puissiez faire ne convaincra au compilateur autrement.
Bien sûr, un C
C
C
c
C code> a une spécialisation pour des pointeurs constants) ...
La chose à garder à l'esprit est que le compilateur est littéralement i> des classes de génération comme je l'ai fait dans la réponse. À un moment donné (il y a longtemps, lorsque le compilateur C ++ ciblé c), il l'a fait dans le préprocesseur. Donc, ce n'est pas que le compilateur joue muet, c'est qu'ils sont littéralement aussi différents que classe A {int A; }; code> et
classe B {int A;}; code>
Oui, je ne vois toujours pas pourquoi je ne devrais pas forcer une distribution de a code> dans
b code> si je sais qu'ils (la façon dont ils sont représentés en mémoire une fois compilé ) sont identiques. Je veux dire si vos classes étaient comme celles-ci:
classe A {int A [1000000]; }; code> et
classe B {int A [1000000]; }; Code> Vous auriez une grande vitesse pour lancer directement
A code> dans
B code>. Étant donné que mes objets peuvent atteindre de grosses dimensions, je voudrais écrire un code qui vérifie statiquement si les types sont compatibles, et s'ils sont, il diffuse:
si (statiquement_compatible :: Valeur) {forcer_cast (A); } else {slow_copy (a); } code> ...
@peoro: Le problème est que rien ne vous garantit qu'ils sont vraiment i> compatibles binaires. Vous pouvez faire des suppositions, vous pouvez même effectuer des affirmations statiques (le type de base des deux conteneurs est de la même taille, ...), mais je ne pense pas qu'il y ait un moyen d'être sûr de 100%.
@Matteo Italia: OK, je l'ai eu. Quoi qu'il en soit, je me sens très frustré à ce sujet. Le compilateur sait si deux objets sont compatibles binaires; À partir du moment où il génère l'exécutable, je peux dire si les choses fonctionnaient sans défaut. Tout ce qui me manque, c'est une construction capable de me dire au moment de la compilation si deux objets sont compatibles binaires. J'utiliserai un conteneur_cast <> code> qui crée une copie du conteneur. Si cela va ralentir les choses (ce que je pense peut-être probablement, car c'est dans ma boucle principale) je vais spécialiser
conteneur_cast code> pour forcer une mise en forme de mémoire, ajout d'unités de test et note partout sur ses risques.
@peoro, @mateo Italia: le problème est plus profond que cela. Même dans les cas où les deux pourraient être compatibles binaires, permettant à la conversion ouvre la langue à d'autres types d'incohérences.
De plus pour de nombreux cas, je suis presque sûr que forcer une réinterpret_cast fonctionnerait bien p> blockQuote>
Je vous parie que ce n'est pas le cas. Deux conteneurs stockés différents types sont jamais em> garantis comme compatibles binaires, même si leurs objets contenus sont. Même s'ils se produisent être compatibles binaires sous une version spécifique d'une implémentation de compilateur, il s'agit d'un détail de mise en œuvre pouvant passer d'une version mineure à la suivante. P>
S'appuyant sur un tel comportement sans papiers ouvre la porte à de nombreuses nuits de débogage désasturées. p>
Si vous souhaitez transmettre de tels conteneurs sur une fonction, faites simplement la fonction un modèle pour que les conteneurs de type arbitraire puissent être transmis. Similaire avec des cours. C'est tout le point de modèles, après tout. P>
Bien sûr que ce n'est pas garanti, mais dans la pratique, tout compilateur stockera un std :: Set
std :: Set
@Peoro: Le problème est double: (1) Ces conteneurs peuvent stocker des informations de type d'exécution; Certes, cela est peu probable en mode de libération (puisqu'il encourt une surcharge) mais tout à fait possible lors du débogage. (2) Il peut exister des spécialisations des conteneurs, même pour des types compatibles binaires qui ont une disposition de mémoire différente.
@peoro: J'ai ajouté un Répondre qui tente d'expliquer que le problème est que, permettant à ce type de conversions briserait la langue de pire des moyens que ce qu'il peut aider. Il y a un exemple concret de pourquoi même si std :: vecteur
std :: vecteur
Ceci est généralement difficile. Le problème devient évident lors de la prise de spécialisation des modèles, par exemple le formidable vecteur
Il n'est absolument pas garanti que ces conteneurs sont compatibles binaires et pouvaient être couplés avec quelque chose comme Par exemple, si le conteneur (comme Interprète donc simplement la mémoire (En outre, il pourrait y avoir des spécialisations de modèles pour différents types, de sorte que Pour convertir un conteneur à un autre, voir par exemple Cette question ou de nombreux autres autres. P> Reterpret_cast <> Code>. P>
std :: vecteur code>) stocke les données en interne dans une matrice de style C,
c
T1 [] code> tableau tandis que
c
t2 [] code>. Si maintenant
t1 code> et
t2 code> avoir des tailles différentes (par exemple
t2 code> a plus de variables de membre) la mémoire du
t1 [] code> ne peut pas simplement être interprété comme un
t2 [] code> car les éléments de ces tableaux seraient situés à différentes positions. P>
C
C
c
C
Ok, ce que je demande, cela ne va pas être possible si t1 code> et
t2 code> sont incompatibles binaires. Je pensais à des cas comme
C
C
Pourquoi ne pas utiliser le moyen sûr puis profil. S'il s'avère être un goulot d'étranglement, vous pouvez toujours revenir sur votre algorithme sous-jacent et peut-être supprimer complètement la nécessité d'une conversion complètement. P> s'appuyant sur tout comportement particulier de REINTERPRET_CAST CODE> peut ne pas causer Des problèmes maintenant, mais des mois ou des années, cela provoquera presque certainement des problèmes de débogage de quelqu'un. P> P>
Ceci est en effet difficile pour les conteneurs. La compatibilité de type n'est pas suffisante, les types doivent en réalité être identique en mémoire pour éviter la tranchée lors de l'attribution. Il pourrait être possible de mettre en œuvre un PTR_Container qui expose les pointeurs d'un type compatible. Par exemple, PTR_Containerers de Boost conserve Cela dit, c'est certainement possible avec des pointeurs intelligents. Par exemple, Void * Code> s en interne de toute façon, donc les jeter à des pointeurs compatibles devrait fonctionner. P>
boost :: partage_ptr code> implémente
static_pointer_cast code> et
dynamic_pointer_cast code>. P>.
Outre tous les autres problèmes traitants par d'autres:
Il y a un problème de base dans l'approche qui n'est pas technique du tout. À condition qu'une pomme soit un fruit, aucun conteneur de fruits n'est un conteneur de pommes (trivialement démontrée) ni un conteneur de pommes ne constitue un conteneur de fruits. Essayez d'installer une pastèque dans une boîte de pommes! P>
Aller à des détails plus techniques et traiter spécifiquement avec l'héritage où aucune conversion n'est nécessaire, (un objet dérivé est déjà em> un objet de la classe de base), si vous étiez autorisé à lancer un conteneur du type dérivé au type de base, vous pouvez ajouter des éléments non valides au conteneur: p> le La dernière ligne est parfaitement correcte: vous pouvez ajouter un Ce n'est pas tout ce qui semble simple dans un premier regard est en fait sain d'esprit. Ceci est similaire à la raison pour laquelle vous ne pouvez pas convertir un Watermelon code> à un
vecteur
watermelon code> à un
vecteur
int ** code> vers un
const int ** code> même si la première pensée est qu'elle devrait être autorisée. Le fait est que, ce qui permettrait de casser la langue (dans ce cas Const Constance): P>
std::vector<int*> v1;
std::vector<const int*> &v2 = v1; // should this be allowed?
const int a = 5;
v2.push_back( &a ); // fine, v2 is a vector of pointers to constant int
// rather not: it IS a vector of pointers to non-const ints!
*v1[0] = 10; // ouch!!! a==10
Eh bien, merci, votre réponse est celle qui me dit le mieux pourquoi je ne devrais pas du point de vue logique, même si cela fonctionnerait dans la pratique. Nous garderons à l'esprit vos exemples, ils pourraient répondre à de nombreux doutes parfois pénétraient dans mon esprit. Mon cas est un peu différent (j'ai besoin de donner l'objet: la détruira lorsque la fonction que je lui donne aux retours - c'est probablement une telle fonction d'être mauvaise conçue, ne savez pas). Maintenant, je sais pourquoi je ne devrais jamais aller chercher une solution similaire dans d'autres cas.
OK, laissez-moi résumer le tout.
Vos réponses (correctes!) Dites qu'en C ++ Compatibilité binaire em> est Également dans la vie réelle, cette chose pourrait être dangereuse, même pour les objets em> simples em>, sans parler des conteneurs! P> *: par compatibilité binaire em> i signifie que les mêmes valeurs sont stockées dans la mémoire de la même manière et que les mêmes instructions d'assemblage sont utilisées de la même manière pour le manipuler. par exemple: même si Cependant, je ne suis pas satisfait de cette règle C ++ : concentrons sur un seul cas, comme sur ces deux structures: Nous ne pouvons pas simplement utiliser l'adresse d'un objet Le compilateur sait statilement si ces structures sont compatibles binaires em>: une fois que l'exécutable a été généré, vous pouviez le regarder et dire si elles sont telles. Juste it (le compilateur) ne nous donne pas ces informations. P> li>
Autant que je sache que tout compilateur C ++ existait a déjà existé des données dans un cohérent em>. Je ne peux même pas imaginer un compilateur générant des représentations différentes pour ces deux structures. Le point qui me bugs le plus est que non seulement ces simples Les opérateurs de casting permettent au compilateur faire ce que je cherche à faire, mais seulement avec des types de base. Si vous lancez un Mon idée est alors de créer une liste personnalisée espoir de ne pas trop être évité pour cette réponse; Je vais supprimer si la communauté ne l'aime pas. P> Pour vérifier si deux types sont compatibles binaires em> introduits un nouveau trait de type: P> float code> et
int code> sont 4 octets chacun, ils ne sont pas compatibles binaires em>. SUB> P>
struct A {int A [1000000]; }; code> et
struct b {int A [1000000]; }; code>. p>
A code> comme s'il s'agissait d'un
B code> un. Et cela me frustre pour les raisons suivantes: p>
A code> et
B code> sont compatibles binaires em>, mais à propos de tout conteneur est, si vous Utilisez-le avec des types que vous pouvez vous attendre à être compatible binaire em> (j'ai rencontré des tests avec GCC 4.5 et CLANG 2.8 sur des conteneurs personnalisés et des conteneurs sur mesure et des stl / boost). P> LI>
int code> comme
const int code> (ou un
int * code> et un
char * code>), et ces deux Les types sont compatibles binaires em>, le compilateur peut (probablement) éviter de en faire une copie et utiliser simplement les mêmes octets bruts. P> Li>
ul>
objet_static_cast code> qui vérifiera si l'objet du type utilisé et que l'objet du type à lancer est compatible binaire em>; S'ils sont simplement renvoyant la référence casted, sinon, cela construira un nouvel objet et le retournera. P>
$ time ./bnicmop
Allocating Data
Destroying Data
real 0m0.123s
user 0m0.087s
sys 0m0.017s
La plupart des conteneurs standard ne prennent pas en charge les types
const code> car ils ne sont pas assignables. Pour les types de pointeur, pourquoi ne pas utiliser le type le plus général que vous devez stocker dans le conteneur? En général, lorsque vous lancez un
t1 code> à un
T2 code> Le résultat est un objet différent de la conversion d'un conteneur de
t1 code> sur un conteneur de < Code> T2 code> implique la copie des éléments contenus. Vous ne pouvez pas éviter cette dépense.
Quelque chose de fondamentalement défectueux dans cette conception que la nécessité de lancer des conteneurs se poserait. Les conteneurs, instanciés sur différents types ne sont jamais garantis pour être compatibles, en même temps, les types qu'ils contiennent peuvent être compatibles ou non. S'ils sont compatibles, lancez le conteneur des objets, pas le conteneur lui-même.