- reinterpret_cast question (with correction!) - 5 Updates
- Why c++ executes destructors of moved objects? - 6 Updates
- Data structure for sparse mapping of values - 4 Updates
- Formatting Ouput with Punctuation (Commas) - 1 Update
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 09 08:40PM +0100 On Tue, 9 Aug 2016 11:19:04 -0700 (PDT) > void * _Ptr = _Access(_Read_access, _Index); > return *reinterpret_cast<value_type*>(_Ptr); > } What "do this" are you addressing? reinterpret_casts are essential for some uses, and the standard describes what aliasing is allowed when using them. The issue you first raised however was that of casting a non-const pair with non-const members to a non-const pair with a const first member, which is a very interesting question. Your extract from Microsoft's code does not help a great deal with that: there are numerous reasons why a reinterpret_cast may have been preferred by the library writer to a static_cast here, and we do not know if it covers your use case. And in writing their standard library, Microsoft can do anything that their compiler will accept, irrespective of whether the code in question complies with the standard or not. Alf's reasoning was imperfect for the reasons I have mentioned but I think his conclusion would still be right if std::pair is an aggregate, for the reasons he has given. However, std::pair has a user defined constructor so it is not an aggregate. Will you get away with it? Probably; my intuition is that you will since it seems so innocuous. It seems pathetic to reject the code simply because std::pair is not an aggregate, and it is difficult to believe a compiler would do so. Is it standard conforming? Dunno, but I suspect not. Chris |
Daniel <danielaparker@gmail.com>: Aug 09 03:16PM -0700 On Tuesday, August 9, 2016 at 3:41:11 PM UTC-4, Chris Vine wrote: > It seems pathetic to reject the code simply because std::pair is not > an aggregate, and it is difficult to believe a compiler would do so. Is > it standard conforming? Dunno, but I suspect not. Thanks, appreciate your thoughtful remarks. It seems to me that the more pessimistic conclusion is most likely to be correct. I do wish the standard associative containers hadn't dictated that value_type must be std::pair<const key, mapped_type>, it was unnecessary to do that, they could have allowed any type that supported key and value accessors and value modifier. As a result it looks like a broad class of associative container implementations cannot be made compatible with standard library associative container iterators, such as the Google C++ btree (which attempts to do so with the cast), stx-btree (which, requiring copying, can't support modifying values through iterators), and (my immediate interest) sorted arrays of pairs. Best regards, Daniel |
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Aug 09 11:26PM +0100 On 09/08/2016 23:16, Daniel wrote: > correct. > I do wish the standard associative containers hadn't dictated that value_type > must be std::pair<const key, mapped_type>, it was unnecessary to do that, It was necessary to do that to ensure that a key cannot be changed once added to the binary search tree. /Flibble |
Daniel <danielaparker@gmail.com>: Aug 09 04:02PM -0700 On Tuesday, August 9, 2016 at 6:26:48 PM UTC-4, Mr Flibble wrote: > > must be std::pair<const key, mapped_type>, it was unnecessary to do that, > It was necessary to do that to ensure that a key cannot be changed once > added to the binary search tree. No. That's an interface issue, not an implementation issue. There is no problem if value_type is defined as a type having a key() accessor that is non-modifying. That's assuming it's the user of the class that you're worried about, who might change the key. If it's a perverse class implementer you're worried about, well, he could always remove a tree node and replace it with a new one with a different key, const key or not. Anyway, this is, while regrettable, unable to be changed. Best regards, Daniel |
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Aug 10 12:23AM +0100 On 10/08/2016 00:02, Daniel wrote: >> It was necessary to do that to ensure that a key cannot be changed once >> added to the binary search tree. > No. That's an interface issue, not an implementation issue. It is both. > There is no problem if value_type is defined as a type having a key() accessor > that is non-modifying. You cannot allow the key to be changed by allowing access to it hence it must be const. > might change the key. If it's a perverse class implementer you're worried > about, well, he could always remove a tree node and replace it with a new one > with a different key, const key or not. You don't get it: if a key in a binary search tree is changed the tree can become corrupt and invalid. > Anyway, this is, while regrettable, unable to be changed. It isn't regrettable as it is the correct design. /Flibble |
Melzzzzz <mel@zzzzz.com>: Aug 09 11:29PM +0200 Given following program, #include <cstdio> #include <utility> class A{ public: A() { printf("constructor\n"); } A(A && moved) { printf("move constructor\n"); } ~A(){ printf("destructor\n"); } }; int main() { A a; A b = std::move(a); } I would expect that destructor of `a` wouldn't be called but: [bmaxa@maxa-pc examples]$ g++ moved.cpp [bmaxa@maxa-pc examples]$ ./a.out constructor move constructor destructor destructor [bmaxa@maxa-pc examples]$ Is this defect, and what is point of moving around, if not? |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 09 11:05PM +0100 On Tue, 9 Aug 2016 23:29:26 +0200 > destructor > [bmaxa@maxa-pc examples]$ > Is this defect, and what is point of moving around, if not? It is not a defect. The destructor for any object must always run when the object is destroyed, say by going out of scope. Typically the destructor for a moved object will do nothing because its resources have been moved out, but it may still have residuary resources to relinquish. The move constructor and assignment operator for a class must ensure that any object of the class is still in a state after the move which is capable of such destruction. It is also wise to design the class so that it can still be assigned to after the move. Chris |
Paavo Helde <myfirstname@osa.pri.ee>: Aug 10 01:34AM +0300 On 10.08.2016 0:29, Melzzzzz wrote: > destructor > [bmaxa@maxa-pc examples]$ > Is this defect, It's not defect, it's a feature! In more detail, the C++ ideology prescribes that the object should be in a valid state during the whole of its lifetime. In case of auto/stack objects this means from the point of their definition until the end of their scope. This is in contrast to e.g. C where often a bunch of uninitialized (invalid state) variables is created right in the beginning of the function, and the pointer values continue to hold now invalid values after they have been free()-d, for example. In par with this C++ ideology, the moved-from objects must still remain in a valid state, as the name of the object is still in scope. This means they may still be accessed, for example a new value may be assigned to the object. If the object were left in invalid state, then in general it could not be assigned a new value (unless it is a POD). As the object is in a valid state, the destructor still needs to be called when the object lifetime ends, to clean up that valid state. If the object has been moved-from, this destructor typically does nothing much, like deletes a nullptr (which is a non-op), but depending on the exact state of the object it might do more. The above is the philosophical point of view. This is reinforced by the technical viewpoint: how should the scope cleanup code know if the variable has been moved-from or not? It might have been moved in one code branch and not in another. Even if moved-from, it might have been assigned a new value after that which needs destruction. The simplest solution is just to call the destructor always when an object goes out of scope, and to rely on this destructor to do the right thing according to the current valid state of the object. > and what is point of moving around, if not? The point is that the resources managed by the object have been moved away, so the destructor has much less to do. HTH Paavo |
Melzzzzz <mel@zzzzz.com>: Aug 10 12:56AM +0200 On Wed, 10 Aug 2016 01:34:15 +0300 > The above is the philosophical point of view. This is reinforced by > the technical viewpoint: how should the scope cleanup code know if > the variable has been moved-from or not? It's solved in Rust pretty clean. It might have been moved in > simplest solution is just to call the destructor always when an > object goes out of scope, and to rely on this destructor to do the > right thing according to the current valid state of the object. That also means that there is unnecessary code for maintaining state and also run time errors in case that object is moved twice... (what one should do if constructing from already moved object?) In Rust moving is done in the compiler and compiler gives errors if moved object is used after move (except assignment). Also compiler doesn't call destructors on moved objects so there is no need to worry about that. > > and what is point of moving around, if not? > The point is that the resources managed by the object have been moved > away, so the destructor has much less to do. Well, yes, but it would be nicer if there would not be need for housekeeping ;) > HTH > Paavo Thank you! |
Melzzzzz <mel@zzzzz.com>: Aug 10 12:57AM +0200 On Tue, 9 Aug 2016 23:05:18 +0100 Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote: It is also > wise to design the class so that it can still be assigned to after the > move. It looks to me like half baked solution. > Chris Thanks! |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 10 12:20AM +0100 On Wed, 10 Aug 2016 00:57:45 +0200 > > wise to design the class so that it can still be assigned to after > > the move. > It looks to me like half baked solution. I don't think I would say it is half-baked. At any rate, it is consistent with the C++ design philosophy. It is also convenient. Not all resources maintained by a class should necessarily be moved out. Furthermore, since in C++ any object can be moved from by casting it to rvalue (ie not only temporaries), if a moved-from object is subsequently assigned to, all its newly acquired resources will need to be cleaned up again. No doubt you could have a language which requires a compiler which deals with (and optimises for) this automatically, but that is not what C++ does. It is not really a problem to write classes this way. I am relaxed about it. You very soon get used to it. |
Marcel Mueller <news.5.maazl@spamgourmet.org>: Aug 09 09:56PM +0200 On 09.08.16 17.44, bitrex wrote: > memory footprint, and it seems goofy to store pointers in memory when > the size of the data structure you're actually interested in is smaller > than the width of a pointer type... std::map uses red black trees. They are intended to keep iterators valid on manipulations. Indeed they consume much memory. => Use a sorted vector with binary search. But, the data type bool[] is bad too as it uses at least one byte per entry.(Simply because common CPUs cannot handle sub byte addressing in a way that complies with the rules for C++ pointers.) => use std::bitset instead. (It depends on the platform if bitset<7> can be packed into a single byte. Some platforms require C++ objects to be aligned at a machine size word.) Furthermore ASCII has only 7 bits. I.e. you need at most 127 entries to cover the entire ASCII range. So if you use a std::array<std::bitset<7>,128> it will take 128 bytes. Really no big deal. In contrast a sorted vector of pair<char,std::bitset<7>> will take twice as much space when all entries are defined. So it is only efficient when you have less than 64 symbols (likely with 7 segments). However the code to do the binary search will probably take more than you saved. The same applies to your initialization code since you did not use constexpr. It will take much more memory than the resulting data structures, including std::map. Unfortunately std::bitset is out at this point since it does not support reasonable constexpr list initializers. So you have to revert to unsigned char, at least for the initialization. Last but not least trivial solutions are sometimes quite effective: switch (character) {case 0: return std::bitset<7>(0b1111110); case 1: return std::bitset<7>(0b0110000); //... } Most compilers will create reasonably optimized code for this. If you want to have even more compact solutions put only continuous runs of values into arrays: static const bitset<7> Numbers[10] = { 0b1111110, 0b0110000, //... }; static const bitset<7> Alpha[6] = // or [26] { 0b1110111, //... }; character -= '0'; if (character <= '9' - '0') return Numbers[character]; character -= 'A' - '0'; if (character <= 'F' - 'A') return Alpha[character]; return bitset<7>(0); Marcel |
Vir Campestris <vir.campestris@invalid.invalid>: Aug 09 09:09PM +0100 On 09/08/2016 19:58, bitrex wrote: > One likes to feel like one is clever, but it does seem like that's the > most straightforward solution... ; ) https://en.wikipedia.org/wiki/KISS_principle Andy |
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Aug 09 09:11PM +0100 On 09/08/2016 21:09, Vir Campestris wrote: >> One likes to feel like one is clever, but it does seem like that's the >> most straightforward solution... ; ) > https://en.wikipedia.org/wiki/KISS_principle KISS is overused, obvious and not always possible: some complicated problems require complicated solutions. /Flibble |
Christian Gollwitzer <auriocus@gmx.de>: Aug 09 10:55PM +0200 Am 09.08.16 um 20:36 schrieb Scott Lurndal: > } else { > return NO_BITMAP_FOR_CHARACTER; > } I second this. The map is definitely not sparse enough to justify a more complex algorithm. Christian |
Vir Campestris <vir.campestris@invalid.invalid>: Aug 09 09:16PM +0100 On 09/08/2016 05:06, Alf P. Steinbach wrote: >>> make them work. >> Do you even READ the posts you are replying to? > It's an ad bot. It is? But it's far too stupid for that... Back to the OP - remember that when you and I see 123.456 it's a hundred and something, to some nationalities that full stop is a thousands separator and the value is over a hundred thousand... number formatting brings nightmares. Nearly as bad as dates. Andy |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment