5 ms·
Unfortunately in C++, the compiler is not allowed to assume that if it passes a const reference to a function, that function will not change the object. The fu
by not2b 7y ago
Unfortunately in C++, the compiler is not allowed to assume that if it passes a const reference to a function, that function will not change the object. The function is allowed to cast away const and modify the object. I'm not happy that they did it that way; I would have preferred it if cast-away-const were more restricted (for example, allowed when calling a child function that takes a char* but doesn't modify the pointed-to C string), with the idea being that if a function has only a pointer to const or a reference for a const object, it has read permission on the object and lacks write permission. But that isn't how the language works.
- saagarjha 7y agoThe fact that std::launder (https://en.cppreference.com/w/cpp/utility/launder https://en.cppreference.com/w/cpp/utility/launder) exists blows my mind. Like, why is this a thing that the standard allows?
- jcranmer 7y agohttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0532r0.pdf http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p053... has an explanation. In short, placement new (or some scenarios involving unions) technically cause undefined behavior if you try to use the object after the call to placement new, since the pre-existing object there has had its lifetime expire. std::launder lets you use a pre-existing pointer to the memory at the same location to access the data there.
- saagarjha 7y agoYes, I know what std::launder does, and that document contains some of my own thoughts: namely, placement new (the only use I've seen suggested) should automatically launder, and std::launder should just not exist.
- gpderetta 7y agoAFAIK placement new does launder. But both new and launder have no effect on their argument (well, placement new of course constructs an object there), they only 'bless' pointer returned from it. The use case is if you placement new on some byte storage. Now you want to access the object stored there, and of course you do not want to placement new the storage again, nor you have cached the result of the previous placement new (it would be suboptimal), instead you want to get a pointer to T from the storage address itself.
- saagarjha 7y ago> AFAIK placement new does launder. But both new and launder have no effect on their argument (well, placement new of course constructs an object there), they only 'bless' pointer returned from it. I may be misunderstanding, but this seems to directly contradict what the linked paper says: > Note that std::launder() does not “white wash” the pointer for any further usage. > The obvious question is, why don’t we simply fix the current memory model so that using data where placement new was called for implicitly always does launder?
- gpderetta 7y agoBecause the placement new might have been called in a other translation unit for example so the compiler can't track the it.
- gpderetta 7y ago> I may be misunderstanding, but this seems to directly contradict what the linked paper says: >> Note that std::launder() does not “white wash” the pointer for any further usage. no, it is consistent. Launder does not white wash its parameter, only its return pointer and any pointer returned by it. Same for placement new.
- klingonopera 7y agoI find it awesome that you can do that, the extra flexibility is great, e.g. when you're working with const char* arrays, sometimes, you need to create a copy of that char array, but save its pointer (char* ) in a (const char* ) variable, but you're still going to need to delete that copy later... and then you're glad that you can simply recast a (const char* ) into (char* ). I save the data of whether or not it's a copy into a flag variable, so my program knows when and when not to delete a const char* . The alternative would be of course to create a two variables, one char* and one const char* , but that would double the memory needed, but only one type of variable would be used at a time.
- account42 7y agoCouldn't you use a union for that? class string { union { const char *referenced; char *copied; } data; bool copied; public: string(const char *source, booly copy) { if(copy) { data.copied = strdup(source); copied = true; } else { data.referenced = source; copied = false; } } operator const char *() const { return copied ? data.copied : data.referenced; } ~string() { if(copied) { free(data.copied); } } }; No const_cast and no wasted memory and it's clear that you are storing two semantically different types. I would expect the conversion operator to optimize down to a noop since in all cases the returned address is equal to this.
- klingonopera 7y agoYes, that could work too, but in my opinion, only unnecessarily complicates things. I want the pointer (actually, that to what it's pointing to) to be referenced as const in all cases, except when I create a copy or delete that copy, in which case I then explicitly recast them as not-const. That's just two tiny exceptions in code I have to write anyways. Such as it is, I would only consider a union of them if I'd actually need const/non-const 50/50 of the time, but my current use case is more like 98/2. Apart from that, I'm guessing you're from a C++ background, as there are no classes in C? (and also, if you use C++, it's recommended to use "delete" instead of "free", since the latter is a C function) EDIT: Actually, it's only one exception/re-cast, when I delete it, as no compiler would complain about saving a char* in a const char* data type.
- jcelerier 7y ago> Unfortunately in C++, the compiler is not allowed to assume that if it passes a const reference to a function, that function will not change the object. how could it make that assumption ? there is nothing that prevents you to implement the function in fortran or even in raw assembly
- MauranKilom 7y agoYou have it backwards. const is your promise to the compiler that the object won't change. It doesn't care whether you const_cast, implement the function in fortran or use raw assembly to mess with that object - it assumes that you don't. If you modify a const object in any way you have violated the C++ standard and get UB in return. Now, the tricky part is that const objects (i.e. objects declared as const) enjoy these optimization benefits, but the compiler usually cannot prove that a reference or pointer to const actually refers/points to a const object. Taking a const pointer to a non-const object, casting away the constness again and then modifying the object is totally legal. Since the former case (objects declared const) is much rarer than the latter case (working on const object pointers/references) the compiler cannot often optimize based on const.
- gpderetta 7y agoInterestingly, top level cost does not guarantee immutability, but any const member reachable from that reference is in fact guaranteed to be immutable: ergo: template<class T> struct deep_const { const T value; }; template<class T> const deep_const& freeze(const T&) { /* magic */ } template<cass T> const T & thaw(deep_const<T> const&) { /* more magic */ } magic and more magic left as an exercise for the reader. edit: I do not think either GCC or clang take advantage of that though, so currently it is just a curiosity. edit: missed actual const keywords /facepalm
- worik 7y agoCast away const. One of the many reasons I gave up on C++. What is const if it can be cast away?
- Kranar 7y agoconst should have been renamed to readonly, because that's what it means. It means that a variable only has readonly access to a value, but it is possible that another variable has write permissions, and so a compiler can not assume that just because one variable has readonly permission, that all variables have readonly permission except for in very limited circumstances.
- bstamour 7y agoreadonly was the original proposed name, if I remember my D&E [0] correctly. Bjarne outlined in that book why he went with const instead, but I forget why off the top of my head. I guess it's tine for a re-read! [0] The Design and Evolution of C++
- gpderetta 7y agoconst values are really const (i.e. immutable). Const references are actually readonly. Could have used different keywords of course.
- MauranKilom 7y ago> The function is allowed to cast away const and modify the object. Correct, but only if the original (referenced) object is not const itself. Casting away constness to modify an object that was declared const is 100% UB.