| ||||
| Comment on GotW #5 Solution: Overriding Virtual Functions by rhalbersma
@Herb If **every** initialization is written in the form auto x = Type { expr }; , isn’t that the same as saying that the auto keyword itself could be left out altogether? What parsing ambiguities could arise if it were? The compiler would only have to check if the lhs of any = token had been seen in the same scope before. Or are there some name lookup issues that would interfere with this? Read More »Comment on GotW #5 Solution: Overriding Virtual Functions by Herb Sutter
@rhalbersma: No, you still need a tag, just as Pascal uses “var” and other languages use other tags. Otherwise, it’s ambiguous or worse. For example, if “x = y;” could be a variable declaration, what if you mistyped x with an off-by-one keyboard error and typed “z = y;” instead? Then anytime you misspelled a variable on the left of an assignment the compiler would happily introduce a new variable and silently compile. Seems like a large trap. Variable declarations do need to be tagged, and four characters is very minimalistic… and unlike Pascal you can declare them anywhere in a scope. Read More »Comment on GotW #1 Solution: Variable Initialization – or Is It? by Kassie
Hi, i read your blog occasionally and i own a similar one and i was just wondering if you get a lot of spam comments? Comment on GotW #5 Solution: Overriding Virtual Functions by rhalbersma
@Herb: I’m not really convinced by the off-by-one keyboard error. If you never reuse that variable z again, the compiler would warn (and give an error with warnings-as-errors) about an unused variable, and if you would use z again, then it might indeed silently compile and do the wrong thing, but auto wouldn’t make a difference. Can you give an example of any real compiler ambiguity? Read More »Comment on GotW #2 Solution: Temporary Objects by GeoffW
@Thomas Gahr: The single pointer implementation points to a structure that embeds the counter, size etc. in the allocated buffer – essentially just your typical pimpl pattern. There are many examples of this – for an easy to read version see the test harness that went with the old GotW#45 (COW_AtomicInt2) – more realistic implementations optimise the empty string case. Read More »Comment on GotW #2 Solution: Temporary Objects by GeoffW
The more I look at SSO the more I wonder just how effective it is – but I’m having trouble finding any discussion of actual testing. It sounds great – no allocation overheads for some percentage of strings – but the cost is that almost every interaction with the string object comes prefixed with (isInternal ? useInternal : useExternal). Yes this is very minor when taken alone, but when added to the extra cost of move, the extra space consumed by even empty strings (think vectors and lists of objects containing strings), it must add up over the life of the object and application. Testing the difference between COW and non-COW was relatively simple, but testing the advantages of SSO is much less so (and may vary widely between applications). Read More »Comment on GotW #4 Solution: Class Mechanics by Herb Sutter
@decourse: f( int ) and f( const int& ) tell the caller the same thing: He provides an int as the first argument, and his int won’t be modified. You ask, why is that in the signature where the caller can see the implementation detail (and must recompile if it changes), even though it doesn’t affect him? So now consider: class widget { /*...*/ private: int x; }; Guru question: Why is x in the class definition where the caller can see that implementation detail (and must recompile if it changes), even though he can’t use it? The two questions are the same. The reason it is necessary to surface this difference in the function signature, even though the caller of the function can’t be affected by it and doesn’t care, is the same reason why we have to make private class members visible in the class definition, even though the caller of the class can’t use them and doesn’t care: Link compatibility requires the compiler knows the calling convention incl. parameter passing and the full layout of all objects used as a value. Even though the caller cannot use such “private” information, it must always be available to the compiler on both sides. The fact that the compiler always knows this is also one of the reasons C++ is a naturally highly optimizable language. Read More »Comment on GotW #5 Solution: Overriding Virtual Functions by jhasse
@rhalbersma Also what about shadowing a variable? Read More »Comment on GotW #2 Solution: Temporary Objects by Herb Sutter
@GeoffW: I don’t know of SSO specific studies offhand, but it’s well known from studies that small strings dominate most applications, and that SSO optimizes two specific things that are well-known to be important: locality, and allocation (for allocation it’s both straight cost plus often poor scaling, though scaleable allocators are getting more common). When Dinkumware switched from COW (which still had bugs) to SSO back around 2000, P.J. Plauger told me about performance measurements he had made that justified SSO, and also that when he made the switch he got no performance feedback from customers. You know that if there were any significant performance regressions in some code patterns then customers would report it. So this means switching from COW to SSO was “a wash” (his words) or better across a very broad set of customer code bases. (And of course switching away from COW put him in a good place because COW was made non-conforming in C++11.) Finally, if you want a little more, remember that: (a) basic_string is a template which means all of its functions are inline; and (b) you usually perform more than just one operation on a string in a given function. So most of the time I would assume that optimizers routinely hoist/eliminate repeated code like isInternal ? useInternal : useExternal checks without even trying very hard. That’s low-hanging fruit for a modern optimizer. Read More »Comment on GotW #4 Solution: Class Mechanics by decourse
Thanks for that answer, Herb. I was searching for an example where the difference between the two different forms of parameter passing may matter, and the only example I could think of is where the class must satisfy some imposed API. Inheriting from an abstract base class is the most obvious example; I haven’t put any thought to whether or not a concept could do it. Hiding the implementation detail by removing the call-by-value is EXACTLY the same thing as hiding the implementation by introducing a layer of abstraction or indirection (e.g. inheriting from an ABC, or implementing the class using a pimpl). That’s the bit I was missing. Read More »Comment on GotW #2 Solution: Temporary Objects by GeoffW
@Herb Sutter: Thanks for your answer. I had not considered locality. Saying “a wash” makes It is easier to understand why libstdc++ may not have rushed into using it – of course the status with C++11 changes that. I note also that consistent use of iterators over operator[] (one of the places where the extra test is potentially significant) would help even if the optimiser should miss it. I’ve learned to be careful about assuming what optimisers may do. I’m looking forward to a review of GotW#33 and discussing inline which has given me some grief lately (it’s often trying to do double duty, normal inline and macro/code replacement, the latter of which can be problematic in the face of optimiser choices). Read More »Comment on GotW #5 Solution: Overriding Virtual Functions by Alok
C++ really needs fixing. While it makes sense to use things from the standard library when it provides one, the amount of thought process that needs to go into C++ programming (whether writing C++ standard library or own code) only shows the weaknesses of the language that blocks the programmer from going into higher level reasoning fast enough. I do not believe that the programmer (of own code or the library) has to think this hard (were the language design optimal) to express some simple concepts like this. Just look at this: “auto pb = unique_ptr{ make_unique() };”, and oh, this is after use of the auto keyword. There are things in C++ that need to be a part of the language itself which are a part of the library instead, and vice versa. I am waiting for the day when some other language like Go or Julia takes over. Read More »Comment on GotW #5 Solution: Overriding Virtual Functions by Sil
@Alok I tend to agree with you that c++ can be extremely hard to understand and to program correctly and as expected without a very sizeable time investment. I also wonder how and if it could be made even simpler and safer than the progresses in 11. On the other hand isn’t it because it is used to design and architect a solution? i.e. if not in the C++ language itself there is still hard thinking to do and to implement for non trivial solutions, notably for modules or libraries, and this hard thinking has to be translated somehow, in guidelines or coding patterns or APIs for example, that will also have to be understood, implemented, checked and that will probably have pitfalls and tricky parts. Is auto pb = unique_ptr{ make_unique() }; a trivial statement? No but should it be used very often in a real life scenario? Probably not. Read More »Comment on GotW #6a: Const-Correctness, Part 1 by earwicker
@Gregory: These instances a, b, are related through their sharing the same counter: IntRef a; IntRef b(a); Such related instances are already liable to cause UB through data race when accessible from multiple threads (banned by the “Dem’s De Rules, Part 1 of 2″ snippet of the standard in Herb’s talk). But specifically, suppose two threads each have their own private std::vector. Thread 1 says: vec1.push_back(a) And symmetrically thread 2 says: vec2.push_back(b) Subsequent calls to push_back on vec1 will potentially call the copy constructor of a again, as the vector may need to reconfigure its internal storage. Similarly for thread 2 and b. As a and b are related, this will cause attempts to increment the same counter, which would be a data race. This is banned by the “Dem’s De Rules, Part 2 of 2″ snippet of the standard in Herb’s talk, because the counter being modified, indirectly, by push_back, has not been accessed via (directly or indirectly) a non-const argument to push_back (indeed, not by any argument passed to the subsequent call to push_back), and yet is accessible to another thread. Read More »Comment on GotW #6a: Const-Correctness, Part 1 by Chris Vine
“Starting with C++11, const on a variable that is possibly shared means read-only, safe to read concurrently without external synchronization.” This is only true for an object not visible in a non-const context to another thread. It is therefore not true of a non-const object passed to a library function by reference to const. If another thread might write on it, a read by the library function is not safe without synchronization. Read More »Comment on GotW #5 Solution: Overriding Virtual Functions by S. Colcord
@Herb: “No, you still need a tag” I wonder, though, if there might be some specific contexts in which the ‘auto’ could be inferred if omitted, because you already know it’s a declaration. For example, within a ‘for-range’declaration’. This would allow the nicely concise syntax: for ( i : mycontainer) {} Read More »Comment on GotW #5 Solution: Overriding Virtual Functions by Michael Urman
@rhalsbersma how would you differentiate between an unreferenced variable whose implicit declaration was due to a typo and an unreferenced variable whose lack of later reference is due to it being a scoped guard? Read More »Comment on GotW #6a: Const-Correctness, Part 1 by mttpd
Just a small fix: s/”const now really does "read-only, or safe to read concurrently"—either truly physically/bitwise const, or internally synchronized so that any actual writes a synchronized with any possible concurrent non-const accesses so the callers can't tell the difference”/”const now really does *MEAN* "read-only, or safe to read concurrently"—either truly physically/bitwise const, or internally synchronized so that any actual writes *ARE* synchronized with any possible concurrent non-const accesses so the callers can't tell the difference” Incidentally, any comments on constexpr-correctness (or is that reserved for the next part? :])? // BTW, there seems to be a few copies of this post. Read More »Comment on GotW #6b: Const-Correctness, Part 2 by mttpd
This is definitely uncompilable (always, regardless of the function’s body), since there’s no such thing as a const-reference (only a reference-to-const), as the reference-referee binding is always invariant: void g( polygon& const poly ) This would also be uncompilable, but this time solely due to the actual body of the function “g” being in violation of the const-contract (which we’ve promised by using the reference-to-const): void g( polygon const & poly ) { poly.add_point( {1,1} ); } Hence, to fix the uncompilable aspect (not commenting on the other aspects here), we can start with: void g( polygon & poly ) { poly.add_point( {1,1} ); } Read More » Comment on GotW #6a: Const-Correctness, Part 1 by Matthew Fioravante
I had a related question concerning std::unordered_map or any similar container. Suppose you have a std::unordered_map and you want to populate it only once at application startup and then only do lookups after that. Accoding to the new definition of const then, all you have to do is make sure your threads only call const methods from the map and that they only do so after the initial population is complete. This can be easily enforced by the compiler by populating the map once before you start the threads and then only exposing a const reference to the map to those threads. No mutexes required at all. The non-const find() can make no guarantees about thread safety. One might imagine a std::map implementation that moves nodes around during lookups without synchronization. In order to be standards compliant and not risk breaking on new platforms/compilers you must use the const methods even if the non-const method for a particular container just happens to work on your system without synchronization issues. The problem is that what if you want to modify the Widgets? Assuming the widgets themselves are internally synchronized by some other means, this should not be a problem. Modifying the value in a key/value pair has no bearing on the map that contains the pairs and does the lookup, as the lookup depends only on the key. However, the const version of find() returns a const_iterator which explicitly prohibits modifying the value. The only way to accomplish this behavior is either by just using a const_cast on the Widgets stored in the container or using some kind of indirection (Storing Widget*, iterators, or some index/key into yet another container). Of course if you indirect into another container, that container lookup has to also be const for thread safety and you’re still stuck. The only technically correct way is to use a raw pointer or introduce your own synchronization (mutex/atomic) somewhere. This usage is not limited to types like map/unordered_map. One might imagine doing the same with a std::vector in order to allocate a bunch of small objects contiguously in memory and then later retrieve and modify them without being able to change the vector. It seems to me the definition of const in this case is a little too strong, but I think the question of whether constness should be “inherited” by the elements of a container is a very old one. I think it made much more sense with the C++98 definition of const. Of course there are times when you want a const map to really mean const keys and values. Was there ever any thought given to this usage pattern when changing the meaning of const? Any guidance on the best way to implement it? My thoughts are wrapping the container in a new type which calls the const methods of the original and uses const_cast before returning non-const references to the members. Read More »Comment on GotW #6a: Const-Correctness, Part 1 by jlehrer
One more typo: const now really does [mean] "read-only, or safe to read concurrently" Read More »Comment on GotW #6a: Const-Correctness, Part 1 by jlehrer
Typo: "can use used safely by multiple concurrent const operations." Read More »Comment on GotW #5 Solution: Overriding Virtual Functions by rhalbersma
@Michael Urman: can you give a code example on how this scoped guard variable would occur? Read More »Comment on GotW #6b: Const-Correctness, Part 2 by Zenju
Buffering “area” looks like premature optimization. One could “fix” the code, make it mutable, add a mutable mutex, but – without further demand – the cleaner solution would be to get rid of it and offer a non-member “double calc_area(const polygon&)” function to keep the polygon interface “minimal and complete”. Interface design would be improved and thread-safety comes as an additional “present”. Read More » | ||||
| ||||
Friday, May 24, 2013
FeedaMail: Comments for Sutterâs Mill
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment