[HN Gopher] What =delete means
___________________________________________________________________
What =delete means
Author : todsacerdoti
Score : 92 points
Date : 2021-10-18 13:07 UTC (9 hours ago)
(HTM) web link (quuxplusone.github.io)
(TXT) w3m dump (quuxplusone.github.io)
| cogman10 wrote:
| So I read the article, and maybe I'm thick... but what does
| `=delete` mean? I saw a bunch of examples about how it will
| trigger errors with l/rvalues in some scenario, but I'm really no
| clear on the meaning of it.
|
| Would someone mind explaining the meaning? Is it some sort of
| lifetime opt-in thing? Is it restricting how a method can be
| called? What's going on here? What is `=delete` telling the
| compiler?
| syncsynchalt wrote:
| To explain from another direction, and add to the replies
| you're getting that explain it directly, C++ is a language that
| does its best to not break existing codebases.
|
| When they want to introduce a new concept (in this case, a way
| to express that using an implicit function or overload is
| forbidden) they use one of the already-reserved keywords
| ("delete"). This sometimes leads to a clunky reading
| experience, but is better than breaking anyone's code that
| happens to use a variable or function named "forbidden" or
| similar.
| TFortunato wrote:
| Yes, this part is super relevant to answer WHY it is written
| the way it is.
|
| C++ has a lot of nice ideas under the hood, but the syntax
| and reading experience definitely isn't ideal because of the
| desire for backwards compatibility (which includes the very
| strong desire to not introduce new keywords / tokens if there
| is any possible way to avoid it)
| jcelerier wrote:
| = delete; allows your users to get a nice compiler error
| messages when they call a function you don't want them to call,
| by mistake.
|
| This can allow for instance to explicitely erase overloads and
| "disable" functions or methods. For instance imagine that you
| don't want a function to be called with a float, just with a
| double, because having floats creeping in somewhere would be a
| bug and you want to catch as many bugs as possible: then you
| can just do the following. void foo(float) =
| delete; void foo(double) { } int
| main() { // won't compile:
| foo(1.23f); // will compile:
| foo(1.23); }
|
| without the following line: void foo(float) =
| delete;
|
| 1.23f would be implicitely converted to a double instead of
| causing a compile error.
|
| One case where I found it super useful is for instance to
| disable constructors taking a pointer when you have ctors
| taking a bool: struct foo {
| template<typename T> foo(T*) = delete;
| foo(bool); }; int main() {
| void* x = nullptr; // won't compile: foo f(x);
| // won't compile: foo g("blablaba"); }
|
| this (rightly) causes a compile error on both the creation of f
| and g, while if foo(T*) = delete; was not there then
| conversions from ptr to bool would happen:
| struct foo { foo(bool); }; int
| main() { void* x = nullptr; foo
| f(x); // compiles :'( foo g("blablaba"); // compiles
| :'( }
| pjmlp wrote:
| A very basic and quite high level explanation is as follows.
|
| In several cases the C++ compiler will generate default
| implementation for specific methods.
|
| However there are cases where that isn't desirable, and until
| =delete came to be, the workaround was to mark the method as
| private without implementation.
|
| So =delete deals away with such hack, although as mentioned
| this is a superficial overview of its purpose.
| cogman10 wrote:
| Ok, that's the C++ I learned many moons back (C++98/03). And
| it makes sense why that exists. Thanks.
| TFortunato wrote:
| You're definitely not thick! This is getting a bit into the
| weeds of lower-level C++, but it is useful. So what delete is
| doing is explicitly saying that a given method (with a
| particular signature) is defined to not exist. It's usually
| used in cases where the compiler would automatically create a
| method for you, or use a method you wouldn't want to be used.
|
| The example they give is the reference wrapper, which (as the
| name might suggest), is used to wrap a reference to some thing.
| To reiterate, with hopefully a bit more of what is going on.
|
| If the creator of this method had just created a function:
|
| template<class T> auto cref(const T&) ->
| std::reference_wrapper<const T>;
|
| Then by C++ overloading rules, you could call this function
| with either an const ref LValue, or an RValue of type T. (In
| their example, they used a number). Which for 95% of functions
| is totally fine, and desired behavior! However, in this case,
| it would be bad, because remember, we are trying to wrap a
| reference to some value (e.g. basically a pointer under the
| hood), so a pointer to some ephemeral value is likely not what
| we want.
|
| In order to solve this, the creator explicitly adds:
| template<class T> auto cref(const T&&) = delete;
|
| Now, if the user calls cref on an RValue like a number, they
| will get a nice error message saying that the function has been
| explicitly deleted. The reason this works is because "const
| T&&" is the better match for the type of an RValue, compared to
| "const T&", so this is the overload that the compiler will try
| to use, rather than the original version above.
|
| Some other reasons to use delete, is if you want to do stuff
| like explicitly forbid the use of copy constructors, because
| you have some object that you don't want copies made of for
| whatever reason. By explicitly deleting the copy constructor,
| you can be sure that no copies are accidentally made somewhere
| in user code (which is especially useful when your code
| interacts with other libraries you don't control)
|
| I hope that made sense, but the TLDR is that there are a lot of
| ways construct objects, delete objects, to copy / move data
| around, etc. If you are doing low-level memory management /
| optimization, you may want to explicitly tell the compiler not
| to do the normal "helpful" stuff it does for you, such as
| creating default constructors / overloads, and this is the
| supported way of doing that in the standard.
| cogman10 wrote:
| Yup that makes a ton of sense, thanks.
| butterisgood wrote:
| Yes, it's a good idea to know the implicit rules for constructors
| and assignment operators in C++.
|
| No, it's not a bad idea to be as explicit as possible all the
| time when writing code, because code is for humans first, and
| computers second.
|
| I always found =delete to be very nice documentation of what I
| can expect of a struct, class etc in terms of valid uses.
|
| And C++ is huge - assuming all the people on your team understand
| all of it, and the corners and implicit behaviors is really not a
| great idea. So you should remove any potential source of
| confusion, or ambiguity in a source tree - as much as possible.
| iulian_r wrote:
| How do you handle failures in constructors without exceptions?
| klyrs wrote:
| The safest & fastest way I'm aware of to use a unique_ptr
| factory. It involves adding an error flag to the class; the
| factory checks the error flag and resets the unique_ptr if it's
| true.
| nyanpasu64 wrote:
| C++ constructors cannot express fallibility. Rust factory
| functions cannot placement-initialize (write into a pointer)
| an object too big to fit on the stack (aside from the perma-
| unstable `box` syntax, which I don't know if it even works or
| not). A unique_ptr factory cannot stack-allocate an object.
|
| I don't know if it's possible to create a safe interface to
| fallible initialization, which is agnostic to whether the
| object will be constructed via return onto the stack, or onto
| heap memory. I'm interested to hear about proposals though
| (I've discussed this in the Rust community discord, but that
| isn't as public or concrete as a long-form article).
| klyrs wrote:
| > C++ constructors cannot express fallibility.
|
| No, they can. They can throw exceptions. My suggestion
| moves unsafe-construction onto the heap. Not ideal for a
| whole host of reasons, including a need to null-check the
| result -- but you can then move the object to the stack,
| and drop the unique_ptr. But if you're tying one hand
| behind your back, contortions will be necessary to
| accomplish the mundane.
| gpderetta wrote:
| You can have a named factory function returning an
| optional<T> (or your preferred fallible type).
| klyrs wrote:
| Right, right... I still haven't adopted the '17 features
| yet.
| ginko wrote:
| In my company we do so by disallowing constructors to fail. If
| an object needs to be initialized with the potential of failure
| you need to call an init() function that returns an error code
| after construction.
|
| Yes, that makes RAII basically impossible.
| kllrnohj wrote:
| Why not a factory function that returns std::optional<T>
| instead? Or better a variant of T or error code?
|
| The construct-then-init pattern is so annoying to deal with.
| ahartmetz wrote:
| IMO the most important part of RAII is really the other side,
| "destruction is resource divestment" - and that still works.
| _That_ is the main purpose of lock guards, refcounting
| wrappers, file objects and so on.
| lkjlakj3334 wrote:
| First of all, I'd avoid exceptions in constructors, regardless
| of what programming language you use.
| SavantIdiot wrote:
| I'm so done with C++. When a fundamental keyword becomes a
| semantic pitfall, you've created an esoteric niche for
| enthusiasts, not a useful tool.
| pansa2 wrote:
| > _I 'm so done with C++._
|
| But what to use instead? Especially in an industry like game
| engine development, where C++ is basically all there is (and
| almost all there's ever been)?
|
| Are we forever going to be stuck with every company trying to
| reduce complexity by defining their own custom subset of C++?
| swalls wrote:
| in the future, I think Jai looks pretty good, as a language
| designed for game dev, considering they're basically battle
| testing it by shipping a large 3d game in it... if the
| compiler ever gets released. I think 7 years of streamed
| development from Jonathan Blow?
| dahart wrote:
| > Are we forever going to be stuck with every company trying
| to reduce complexity by defining their own custom subset of
| C++?
|
| Yes, probably? In 30 years of professional coding and a
| decade of game engine development, I've never seen that not
| be the case, all the companies I've been at and all the
| companies I know about limit their C++ to a subset to try to
| control complexity.
|
| I'm curious if you're suggesting it would be better to
| increase complexity by allowing all of C++? There are
| definitely features I don't trust everyone with.
|
| Complexity combined with engineer hubris is one of the big
| problems with C++. While working in games, I witnessed many
| people overengineering and being too clever by half, and
| costing the team time and money. One of the most memorable
| bugs I ever tracked down while working in games was a
| release-build only crash where a programmer had tried to get
| fancy with a copy constructor and bungled it unknowingly. We
| had a team of 10 people working over a weekend trying to
| catch it and I had to write a custom debugger to trap the
| call stack. The cost of his trickery was easily in the
| several tens of thousands of dollars at least. It only takes
| that happening a few times before you realize C++ is a foot-
| gun in many (most? all?) hands.
| pansa2 wrote:
| > _I'm curious if you're suggesting it would be better to
| increase complexity by allowing all of C++?_
|
| No, definitely not. I'm wondering if there might be a
| language on the horizon that's suitable for game engine
| development but which is substantially _less_ complex than
| C++.
|
| Or, if not, is there an effort to rally round a particular
| subset of C++ instead of everyone defining their own? In
| your experience, have the C++ subsets used in different
| companies been very similar, or quite different?
| TuringTest wrote:
| _> No, definitely not. I'm wondering if there might be a
| language on the horizon that's suitable for game engine
| development but which is substantially less complex than
| C++. Or, if not, is there an effort to rally round a
| particular subset of C++ instead of everyone defining
| their own?_
|
| If it doesn't exist, it should be created and called _--
| C_.
|
| (C-- is already taken) https://en.wikipedia.org/wiki/C--
| nynx wrote:
| The main alternative is likely rust.
| criddell wrote:
| Are you saying that for game engine development, Rust is
| the second most popular option?
| SavantIdiot wrote:
| I didn't say "You can't use it," I said "I" am done with it.
|
| Have fun! Write some cool games!
| pansa2 wrote:
| Thanks for the encouragement, it's appreciated.
| fullstop wrote:
| Someone is going to say Rust, and I might agree with them at
| some point in the future.
|
| I periodically check https://www.areweguiyet.com/ to see the
| state of things, but C++ is still king here.
|
| I cut my teeth on C++ but was then employed to write code in
| C. After almost two decades of doing this, C++ looks vastly
| different to what I remember from 2001 and kind of ugly to
| me. I'm sure, however, if I had stuck with C++ for the last
| twenty years it would feel like home.
| jerf wrote:
| The places where you _must_ use C++ are a dwindling niche.
| Large niches in their own right, certainly, but niches. Once
| you leave those niches there is an explosion of
| possibilities.
|
| I'm coming up on year 25 of my career and I haven't touched
| C++ since school, if one can indeed call what is covered in
| school C++. Especially nowadays. And C and Java I've _barely_
| touched. I certainly couldn 't put them on my resume with a
| straight face.
|
| I say this because you kind of sound like you're making some
| sort of plaintive plea, as if C++ is somehow the only viable
| option in the world and who could even dream of stepping
| outside of it? And I'm telling you that while that may be
| true in an ever-decreasing set of niches, it is not true in
| general anymore and hasn't been for a long time.
| professoretc wrote:
| > Are we forever going to be stuck with every company trying
| to reduce complexity by defining their own custom subset of
| C++?
|
| The problem with doing this is the same as with the idea that
| one "90% of people only use 10% of Excel's features" so we
| should be able to make a simpler Excel and capture 90% of the
| market: all those people use a _different_ 10% subset. It 's
| the same with C++; everyone wants a "simpler" language, but
| no one agrees on what should be kept and what should be
| thrown out. C++ is the language that results from putting
| together everything that everyone needs (and then dealing
| with the resulting conflicts and contradictions).
| edflsafoiewq wrote:
| What is the semantic pitfall here?
| beached_whale wrote:
| For almost everyone, none. delete doesn't remove the method
| from the overload set, it leaves it there in order to give
| the caller an error saying don't do that.
| klyrs wrote:
| It also prevents the compiler from emitting a default
| implementation for the deleted method.
| tialaramex wrote:
| C++ has a keyword, "delete" which originally refers to an
| operator that destroys objects, thus calling their destructor
| and giving back resources (most obviously memory) allocated
| for their lifetime.
|
| But, adding keywords to C++ is expensive. You can't use these
| as symbols, you can't name a class, a variable, a function or
| anything "delete" because that's a keyword and so it's
| reserved. A new keyword would clobber existing symbols, and
| that's painful, so, C++ tries not to do it, instead
| repurposing existing keywords.
|
| So as well as the operator named "delete" now "delete" refers
| to this feature where you can tell the compiler that this
| particular overload mustn't be used, if it was looking for an
| overload, it won't use this because you explicitly said not
| to (even if it could have otherwise conjured a default) and
| if somebody else called it by mistake now they get a compile
| error saying not to.
|
| I don't think they would naturally have written =delete if
| not for being conscious that doing so is "free" (the word is
| already reserved for something else) whereas some other word
| would have caused compatibility problems.
| cesarb wrote:
| > A new keyword would clobber existing symbols, and that's
| painful, so, C++ tries not to do it, instead repurposing
| existing keywords.
|
| It's even more painful than that. A new keyword could
| conflict with a _macro_. So even contextual keywords
| (something which is a keyword only in a place where
| arbitrary symbols are not allowed, so there 's no conflict)
| could be problematic.
| edflsafoiewq wrote:
| What, you think the syntax is funny? I guess, but that's
| not a _semantic_ pitfall.
| tialaramex wrote:
| Previously delete referred to an operator
|
| Today delete is both the operator and this entirely
| unrelated feature, so that alters the meaning, a matter
| of semantics.
|
| Natural languages have plenty of such ambiguity, but C++
| has a Committee (yes I know about French, no the Academie
| Francaise doesn't actually get to decide how French
| works, that's not how natural languages work) which could
| have chosen to use a different word and did not.
| [deleted]
| edflsafoiewq wrote:
| A semantic pitfall would be something where you think the
| code means one thing (a patch of grass) but it really
| means another (you fall into the spikes). =delete is
| neither; it is not possible to confuse it for operator
| delete, and I don't see even a hole to get your foot
| stuck in, let alone any spikes.
| squeaky-clean wrote:
| > it is not possible to confuse it for operator delete,
|
| I haven't written C++ in many years and my interpretation
| of the code examples before reading the rest of the
| article was that it was somehow binding it to the delete
| operator as a function.
| tialaramex wrote:
| Fair. I wouldn't have chosen the phrase "semantic
| pitfall", however after some thought I think it would
| classify as a "hole to get your foot stuck in" because
| it's unexpected in an artificial language to have this.
| If you're a learner and you covered "delete" the operator
| last week, then missed a day, and now there's = delete on
| the slides, do you raise your hand to ask what's this
| completely new syntax? No, right, it must somehow be the
| delete operator you learned about, as otherwise it would
| have a different name... wouldn't it?
|
| But sure, C++ does have actual hidden spike traps, where
| unwary programmers are going to hurt themselves badly by
| mistake and this is not one of those.
| jcranmer wrote:
| Do you also complain that the operator named '*' is
| overloaded? As a binary operator, it means to perform a
| multiplication operator. As an unary operator, it means
| to load the value located at that memory address, which
| has nothing to do with anything close to multiplication.
| That is a more gross alteration of semantics than the use
| of a keyword in completely different slots (as an
| expression versus as a declarator).
| tialaramex wrote:
| I'm not a fan of your example with the asterisk or of the
| reference operator sharing a symbol with the binary AND
| operator.
|
| I don't like the use of + for concatenation much either,
| even though it's overloaded to do that in some languages
| I really like, and it's even _special cased_ despite the
| lack of overloading in one language I think is fairly
| good (Java) and some others I 've used but think are
| garbage.
|
| But I'm willing to cut some more slack for symbols
| because they're short.
|
| I think erase for example would have been a better choice
| here (I haven't spent long thinking about this, the
| committee had months), _but_ C++ had to worry about the
| backwards compatibility penalty and that 's... sad.
| agent327 wrote:
| Actually it would have been a contextual keyword, meaning
| it could be any word the committee wanted it to be, and
| it is only recognized as such in this particular context
| (i.e. at the end of a function declaration). They could
| have chosen anything they wanted, but decided that
| '=delete' was fine.
|
| Other examples of contextual keywords are 'final' and
| 'override', which may also occur on function
| declarations.
| hnfong wrote:
| I guess they mean the "syntax-semantic mismatch pitfall"?
| :)
| jmull wrote:
| A pointless semantic argument about the word semantic...
| Thank you C++!
| agent327 wrote:
| It's just a piece of programming knowledge being explained.
| It's very far from a 'semantic pitfall' or an 'esoteric niche'.
| Don't be such a drama queen.
| carpenecopinum wrote:
| If a language feature being weird is enough to declare that
| you're "done with it", you won't have too many languages to
| work with in the end. Maybe some LISP, I guess?
|
| You don't become a good craftsman by rejecting every tool that
| isn't perfect. You become one by knowing your tools and their
| flaws and strengths well.
| SavantIdiot wrote:
| > You become one by knowing your tools and their flaws and
| strengths well.
|
| Yes, that's why I said "I" am done with it, not "you should
| be done with it." There's no need for me to use it ever.
| Except when forced to with mbed, TFLiteMicro, and the
| occasional port of an Arduino driver to C.
|
| I've been at this since before C++ existed. I've watched it
| come in to the world, and I've watched it mutate, Akira-like,
| into an academic omphaloskepsis.
|
| EDIT: Fun story: I learned C++ in ... 1990? ... by watching a
| series of videos on VHS tape taught by none other than Bjarne
| Stroustrup himself. I think it was a bonus that came with the
| purchase of the first version of the Borland C++ compiler for
| OS/2.
| gpderetta wrote:
| There is no semantic pitfall in = delete. = default on the
| other hand...
| codebolt wrote:
| I'm with you. The only thing = delete means to me is that I'm
| happy C++ isn't part of my dayjob.
| jcelerier wrote:
| > I'm so done with C++. When a fundamental keyword becomes a
| semantic pitfall, you've created an esoteric niche for
| enthusiasts, not a useful tool.
|
| you think quux wouldn't be able to find semantic pitfalls in
| literally every language you would use instead ?
| tux3 wrote:
| Some language designs have more semantic pitfalls than
| others. All else equals, those would be preferable.
|
| C++ is an old beast with many many footguns.
|
| There's a more modern subset hiding inside, these days, but
| let's not pretend every language has the same pitfalls that
| C++ has.
| jcelerier wrote:
| sure, but there's also a deep culture in the C++
| blogosphere to go and seek the deepest and darkest corners
| and exhibit them as it's a very fun puzzle game. the same
| people applied to Python, Java or LISP would yield endless
| articles on pitfalls of those
| SanFranManDan wrote:
| If they are endless where are they?
| pjmlp wrote:
| Search for Java Puzzles books
| tragomaskhalos wrote:
| In the early 90's there were two print magazines: "The
| C++ Report" and "The Smalltalk Report". The former, even
| with the far simpler language it was then, was chock full
| of programming arcana and 'what does this code do?' type
| puzzles. The latter, by contrast, had effectively zero
| such content. I loved both languages but this difference
| was always very stark.
| mikepurvis wrote:
| I think for most of us mere mortals, the only normal use for
| =delete is making a class non-copyable. And for that, my muscle
| memory is already wired to inheriting from boost::noncopyable
| anyway.
| jpm48 wrote:
| I put
|
| private : myclass(const &myclass )=delete;
|
| Just to be safe :-)
| ziml77 wrote:
| Funny thing there is that one could read that as saying
| that the copy constructor's deletion is private and
| therefore still accessible in the public interface. I
| wouldn't put it past C++ to do something as crazy as that.
| bialpio wrote:
| IIRC this will make the compiler error less helpful than it
| could be? I think you'd get an "attempt to use private
| member" instead of "attempt to use deleted member". End
| result is probably the same, but it feels like you're not
| saying what you mean with the code.
|
| Edit: NVM, looks like all major compilers will say that the
| copy ctor is deleted. https://godbolt.org/z/jTd3714Tb
| Psychlist wrote:
| clang-tidy will ask that you put in the full set.
| Foo() = delete; Foo(const Foo& orig) = delete;
| Foo(Foo&& orig) = delete; Foo operator=(const Foo
| &other) = delete; Foo operator=(Foo &&other) =
| delete;
|
| (add const to taste)
| simias wrote:
| I must say that I'm always a bit in awe when I read modern C++
| and I realize that I barely understand what's going on even
| though ~10 years ago I considered that it was the language I
| knew best. And even back then it was already a sprawling
| monster of a language. Nowadays the idea of having to maintain
| a large C++ codebase scares me. Too many footguns and arcane
| knowledge required.
| kllrnohj wrote:
| Eh? '=delete' is a bad example of "footgun" or "arcane
| knowledge". There are those in C++, sure, but this isn't one
| of them.
|
| You can happily never know the existence of '=delete' and
| nothing about your code changes. And if you ever hit a
| libraries usage of it, like the standard library, you get a
| pretty clear error message at compile time instead of an
| actual footgun in C++98 like memory corruption or runtime
| crashes.
|
| '=delete' is basically the entire reason why std::unique_ptr
| is safe and std::auto_ptr is a footgun. It _reduces_ the ammo
| aimed at yourself & the amount of knowledge you need to know
| (like the knowledge to never put auto_ptr in a vector)
| simias wrote:
| I was talking generally, not for this particular feature.
| But it would still be sort of arcane knowledge for me since
| when I last actively used C++ `=delete` either didn't exist
| or was not in common use, so if I encountered it in some
| codebase I'd probably guess what it does but still have no
| idea about the implications.
|
| Like, are there other =<something> construct? Is it
| overridable or overloadable by the user? Can I use it to
| "erase" any member of a class, for instance to hide a
| parent member from an inherited class?
|
| I suspect that the answer to all of these questions is
| "no", but I can't know for sure without deep diving into
| some C++ ref.
|
| In the C++ I knew there was nothing resembling this syntax,
| and `delete` was only the keyword you used to free memory.
| kllrnohj wrote:
| I would argue that's just "knowledge" not "arcane
| knowledge". To me arcane knowledge is stuff like the
| xvalues, prvalues, etc... Where it's both complex and
| obscure. Where you can go down a headache-inducing rabbit
| hole. This isn't really that, it's pretty simple, and
| really only does that single thing. It doesn't have
| complex interactions with anything else: https://en.cppre
| ference.com/w/cpp/language/function#Deleted_...
|
| As a sibling mentioned, it's not even a new syntax as '=
| 0' has always been there.
| cesarb wrote:
| > Like, are there other =<something> construct?
|
| Yes, there's "= 0" to mark a virtual method as not
| implemented in this class (that is, it must be
| implemented by a subclass), which is older than "=
| delete". Conceptually, it sets the corresponding slot in
| the virtual method table to null instead of a pointer to
| the (non-existent) method, except that it actually
| doesn't do that, it sets that slot to a pointer to a
| "pure virtual method called" function from the standard
| library, which AFAIK prints an error message and exits
| the program (IIRC, this is because there are some
| situations in which you can actually manage to call such
| a pure virtual method, like calling it from a function
| called within the constructor or destructor of the base
| class.)
| jdsully wrote:
| A concrete object must implement all virtual methods
| including those marked as pure virtual. All =0 means is
| that someone else lower down the inheretence chain will
| implement this and the caller using the class type can
| rely on it existing.
|
| If you try to instantiate an object without all methods
| implemented you'll get a compiler error about the class
| being "abstract". You should never have a case where
| there's a null in the vtable.
|
| If there is some specific edge case around ctors then
| forgive me - this is C++ after all! But in practice you
| don't have to worry about this which is true for most of
| the C++ minutia.
| cesarb wrote:
| > If there is some specific edge case around ctors then
| forgive me - this is C++ after all!
|
| Yes, the specific edge case is around ctors and dtors.
| During the ctor or the dtor, the vtable is the vtable of
| the base class, not the derived class, so if you call a
| virtual function within the ctor or dtor of the base
| class (don't do that), the function which will be called
| is the one from the (possibly abstract) base class. See
| for instance this FAQ entry:
| https://isocpp.org/wiki/faq/strange-inheritance#calling-
| virt...
| gpderetta wrote:
| In addition to =0 mentioned elsethread, there is also
| =default which will force the generation of a default
| function that would be otherwise suporesed.
|
| You can use =delete to delete any overload of a function,
| be it class member or namespace scope.
|
| Languages evolve; =delete and default were added to get
| rid if a bunch of common hacks and the language is better
| for that (at least for delete, default has a bunch of
| pitfalls of its own unfortunately).
| pjmlp wrote:
| Until one delves into all the cool niche features being added
| into Java, C#, F#, Python,...., even C.
|
| Unless we are talking about v1.0 there always such cases when a
| language is around long enough.
| enriquto wrote:
| > I'm so done with C++. When a fundamental keyword becomes a
| semantic pitfall, you've created an esoteric niche for
| enthusiasts
|
| but it always was!
| kccqzy wrote:
| How is it a pitfall? The article clearly elucidates several
| uses of the "=delete" construct. I don't see any pitfalls, just
| some new uses of the feature. And I expect most people to be
| the same: most people only ever use "=delete" on the copy
| constructor and the copy assignment operator, now this article
| tells them you can actually use it on other things too. There's
| no pit to fall into, just new ways to use a feature.
| cletus wrote:
| It's well known that no one really programs in C++. They program
| in whatever subset of C++ their organization allows, their brain
| can handle or both.
|
| Personally I found Google's C++ dialect to be sane, even
| pleasant. No exceptions, no mutable reference parameters (side
| note: this was a giant language fail that foo(f) could be a const
| reference or a non-const reference and there's no way to know
| without looking at the declaration), pervasive use of union types
| (absl::Status/StatusOr are the open source versions) and, here's
| the big one, individual teams were expressly forbidden from
| creating their own templates.
|
| Now compare this to Facebook's dialect: exceptions allowed,
| mutable reference parameters allowed (side note: people weren't
| strict with const-ifying reference parameters so you actually
| didn't know if something was actually mutated or not), functions
| that routinely just return bool (so helpful) and you can create
| your own templates.
|
| But seeing this post makes me see the wisdom in not only how cut
| down Google's dialect is but also that stopping teams creating
| their own templates is the only sane choice.
|
| The depth of knowledge required to create templates that don't
| behave unexpectedly (eg dangling references, redundant copying,
| useable with move semantics, etc) is so large that only
| specialists should engage in it.
|
| There is a certain breed of programmer who will use a feature of
| a language because it's there. They will view esoteric features,
| unnecessary complexity and brevity over readability as not only
| virtues but goals with which they can tell the world how smart
| they are.
|
| If you write code for you, you're either doing it alone or you're
| not a team player. You write code for the next person who comes
| along and has to figure out why it's broken or just how it works,
| not to be as clever as you would like to think you are.
| ruohola wrote:
| > functions that routinely just return bool (so helpful)
|
| I don't get it? What's wrong with returning a bool?
| Kranar wrote:
| There's this pretty abstract idea of boolean blindness, which
| basically boils down to the idea that a boolean by itself
| must always be associated with an interpretation and said
| interpretation is external to the boolean. Afterall a boolean
| by itself is just a single bit of data, 0 or 1, how to
| interpret that bit is entirely independent of the bit itself.
|
| This means anytime you operate on a boolean, you must somehow
| recover its interpretation from somewhere, and the argument
| is that such recovery is error prone and fragile.
|
| The preferred way, presumably, is then to encode the
| interpretation directly into the type, so for example you use
| an enum along with pattern matching, or you write a type that
| wraps a boolean with some value (like a Maybe/Optional type).
| In effect anytime you have a boolean, you also carry with it
| its interpretation side-by-side so you don't need to go
| figuring out how to recover it.
|
| This article goes into it in more depth:
|
| https://existentialtype.wordpress.com/2011/03/15/boolean-
| bli...
|
| I personally think it's insightful and useful, but there are
| also downsides to it as well, for example when you need to
| operate on multiple booleans, it becomes unergonomic and
| bloated to deal with pattern matching over multiple enums or
| having to write out a lot of redundant code instead of
| operating on boolean operators.
|
| Basically consider these two options:
|
| Option 1: boolean blindness. bool
| isEven(int value); ...
| if(isEven(x)) { print("Even"); } else {
| print("Odd"); }
|
| Option 2: Enum enum Parity {
| EVEN, ODD }; Parity
| getParity(int value); ...
| switch(getParity(x)) { case: Parity::EVEN:
| print("Even"); break; case:
| Parity::ODD: print("Odd"); break;
| }
|
| You can decide which if the two options above is more
| appealing to you in terms of reading and writing.
| cletus wrote:
| This applies to function parameters and return values: you
| almost never want to use a bool. Instead you want to use an
| enum with two values. Booleans are hard to read in most
| cases. Obviously there are trivial cases where this isn't the
| case (eg bool isDigit(char) is completely fine).
|
| Contrived example: bool
| connectToHost(string hostname, bool useSsl);
|
| First problem: if it fails, why does it fail? Some will be
| tempted to throw an exception. Better is to return some kind
| of status.
|
| Second problem: you add another version of SSL, now what? If
| you'd used an enum, it's just another value. If not, you now
| need to retrofit it.
|
| Better version: absl::Status connect(string
| hostname, EncryptionType encType);
|
| People will often make mistakes when a function has 3 or more
| booleans and put the wrong value in the wrong parameter. With
| strongly typed arguments, this becomes a compiler error.
|
| People will extend booleans to add a third value. In Java,
| for example: Optional<Boolean> foo
|
| But it doesn't end there. As I like to say, for when three
| values for your boolean just aren't enough:
| @nullable Optional<Boolean> foo
|
| Just start with enums.
| Psychlist wrote:
| > People will often make mistakes when a function has 3 or
| more ...
|
| of anything. This is why non-compatible numeric subtypes
| are so valuable. Code like "quantity_sold = price" should
| not compile. You can do the same thing for other types
| using the same template but IME that's somewhat less common
| (but does happen: user_name = address!!).
|
| It would be nice to get a variation on std::pair that works
| in a similar way to Rust's std::result<value, error>,
| because std::optional is just a dressed up boolean. I did
| just swipe the std::pair declaration and do that, but
| having it official would be nice. Especially for the non-
| throwing version of the standard libraries.
| dragonwriter wrote:
| > People will often make mistakes when a function has 3 or
| more booleans
|
| A function with three or more positional parameters in a
| language that isn't badly broken (that is, one that
| supports keyword and/or structured parameters, which pretty
| much all significant languages do) is, IMO, a code smell.
| Even if more specific typing and IDE pulling up signatures
| can makes it less likely to make usage errors, readability
| is impaired.
| UncleMeat wrote:
| > individual teams were expressly forbidden from creating their
| own templates
|
| When were you at Google? I've been here a lot of years and
| never ever heard this. My codebase has a fair amount of
| templated code, including some metaprogramming magic. Nowhere
| has any tool said "please don't do this", nor do I think it
| should. Complex template code can be an absolute nightmare to
| maintain, but there is a group of C++ experts that are
| available to help if it is indeed the best solution to a
| problem and you want to devise a maintainable implementation.
|
| The closest in the style guide I can find is "Avoid complicated
| template programming," which is clarified to include
| metaprogramming magic. This is very different than what you
| write and not even "forbidding" it.
| cletus wrote:
| 2010-2017
|
| Certainly at that time, creating your own templates was
| explicitly disallowed by the C++ style guide (ie readability
| requirements). The public style guide mentions template meta-
| programming specifically [1]:
|
| > The techniques used in template metaprogramming are often
| obscure to anyone but language experts. Code that uses
| templates in complicated ways is often unreadable, and is
| hard to debug or maintain.
|
| This certainly applied to Google3. Non-Google3 C++ code bases
| could and did have their own standards and style.
|
| General note: templates were created but by library/framework
| teams who specialized in that. The way I like to put it,
| creating templates was a job for branch nodes not leaf nodes.
|
| [1]: https://google.github.io/styleguide/cppguide.html#Templa
| te_m...
| pxx wrote:
| This restricts metaprogramming, not you putting "template
| <typename T>" in your code for a generic function. I don't
| know how you got such a weird read of the readability
| requirements; it is definitely not as strict as you put
| here (and I'm currently at Google and commonly write C++).
| UncleMeat wrote:
| "Template meta-programming" and "templates" are very
| different. Further, the style guide says "avoid" but does
| not forbid it. Clang-Tidy doesn't throw up any warnings
| when using things like std::enable_if.
|
| My codebase overlaps with the majority of your tenure at
| Google. We are in google3.
|
| I do agree with the guidance that things like SFINAE should
| be avoided in general, but I believe you've way overstated
| the rigidity of the guidance.
| petters wrote:
| Could you possibly be misremembering?
|
| I think creating e.g. template<F> F MyComputation(const F&
| x) would be allowed e.g. for supporting both float and
| double. It would also be required when using the Ceres
| optimization library.
| codeflo wrote:
| I can't contribute to the discussion about Google's rules.
| I just want to mention that simple uses of templates for
| generic data structures are very different from template
| meta programming. I can see how a style guide might allow
| the former and ban the latter.
| jeffbee wrote:
| My C++ readability changelist contained a template. I don't
| think this part of the style guide is what you thought it
| was.
| rpangrle wrote:
| I'm a current Googler with C++ readability and I can
| confirm, you're absolutely allowed to write templates in
| "leaf node" projects and it's encouraged, especially in
| cases where templated code reduces error-prone boiler
| plate. Heck, there are whole Grow classes dedicated
| around learning how to use variadic templates.
| einpoklum wrote:
| > They program in whatever subset of C++ their organization
| allows
|
| So, no-one writes C++ except for some organization that tightly
| controls their work? :-( And no large organizations allow for
| some coding autonomy in writing, say, the insides of libraries
| and components?
|
| > Google's C++ dialect
|
| With changing language versions, dialects change. For example,
| union types: With C++17 you have variants; which are still a
| bit painful compared to other languages' union types, but are
| usually an improvement over just using unions and crossing your
| fingers you've always been careful with them.
|
| > side note: this was a giant language fail that foo(f) could
| be a const reference or a non-const reference and there's no
| way to know without looking at the declaration
|
| C and C++ have a lot of implicit type conversions. It's fair to
| be against that, but it's not like this specific aspect is a
| "giant fail" in itself; it makes sense, consistensy-wise.
|
| > Facebook's dialect:
|
| Those aspects you describe are indeed annoying, but, again -
| are we talking about recent code?
|
| > The depth of knowledge required to create templates that
| don't behave unexpectedly ... is so large that only specialists
| should engage in it.
|
| I disagree. If you're not obviously careless, your templated
| types will be reasonably well-behaved.
| gjulianm wrote:
| > here's the big one, individual teams were expressly forbidden
| from creating their own templates.
|
| What do you mean by this? General template code was forbidden
| or just templated containers, STL style?
| db48x wrote:
| Means exactly what it says. You cannot create templated
| functions, classes, etc.
| gjulianm wrote:
| That seems extremely restrictive. I get being careful
| around heavy use of templates, and even more with
| containers where allocation/copies/moves can be really
| tricky; but there is a lot of space for using templates in
| a simple but useful way.
| pxx wrote:
| These are allowed. I have no idea where the parent post
| gets the idea that you can't ever write a template.
| kllrnohj wrote:
| > Personally I found Google's C++ dialect to be sane, even
| pleasant. No exceptions,
|
| Google's C++ dialect itself calls this out as a bug, not a
| feature:
|
| "On their face, the benefits of using exceptions outweigh the
| costs, especially in new projects"
|
| https://google.github.io/styleguide/cppguide.html#Exceptions
|
| EDIT: Personally I like no exceptions normally but there's
| definitely classes of problems where exceptions are
| overwhelmingly superior, such as anything hitting files or a
| wire (eg, IPC). Serialization code is incredibly tedious to do
| error checking after every read or write call. This is where I
| think the "throws" proposal strikes the right balance:
| http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p070...
|
| > and, here's the big one, individual teams were expressly
| forbidden from creating their own templates.
|
| There's no such rule. It just says avoid them if you can,
| otherwise go for it:
| https://google.github.io/styleguide/cppguide.html#Template_m...
|
| And if there was such a rule, then Abseil wouldn't exist. You
| don't end up with your own template library by telling people
| to never write templates.
| cletus wrote:
| On exceptions, I respectfully disagree. Even if you don't use
| exceptions in C++, just allowing them (and thus supporting
| them) imposes a cost, both in terms of extra code but more
| importantly on the programmer to make sure what they write is
| exception-safe.
|
| For a large number of people, exceptions is essentially
| interchangeable with "output log message and exit".
|
| > And if there was such a rule, then Abseil wouldn't exist.
|
| You misunderstand me. What I call "leaf nodes" don't (or
| shouldn't) create their own templates. By "leaf nodes" I
| mean, say, the payments logging team. They're created by
| specialists. Specifically, abseil is created from "//base",
| which is a mature battle-tested set of libraries at this
| point.
|
| Likewise, anyone can create something in base or just modify
| something that's there but any such addition or change is
| going to go through an awful lot of scrutiny and testing.
|
| That's the point.
| kllrnohj wrote:
| You don't start by building something general in a base
| library. You start by solving your local problem, and if
| that turns out to be a general problem it should then be
| promoted to a base library. Battle-tested set of libraries
| started out their life as random utilities in "leaf nodes"
| - otherwise they wouldn't be "battle-tested". See also
| https://blog.codinghorror.com/rule-of-three/
| tsimionescu wrote:
| Those are two well-known approaches to problem solving:
| GP is proposing top-down, you're advocating for bottom-
| up. There are pros and cons to each, and in the end it is
| more of a matter of preferences.
|
| Just to give an example of someone else advocating for
| top-down problem solving, Bjarne advocates strongly for
| designing the libraries that you will need before writing
| specific code for your problem. He has some example
| videos where he goes through a code review taking some
| piece of procedural business code and showing how it
| should have been written to take advantage of existing
| libraries and creating new ones before.
| hashingroll wrote:
| > no mutable reference parameters
|
| Sidenote: The style guide (recently?) removed the ban on non-
| const references [0]. They are now allowed for non-optional
| output parameters. Though returning value is generally
| preferred over output parameters.
|
| [0]
| https://google.github.io/styleguide/cppguide.html#Inputs_and...
| mgraczyk wrote:
| > individual teams were expressly forbidden from creating their
| own templates.
|
| Just want to underscore what others are saying, that this is
| not true and was not true during the time you claimed to work
| there. I was at Google 2014-2016 and I'm there now. Templates
| are common and not banned at all. I committed template-heavy
| non-metaprogramming code to google3 in 2014 and also throughout
| this year. I have never had C++ readability.
|
| "Template Metaprogramming" is soft-banned in non-library code,
| meaning that it is disallowed if the readability reviewer
| looking at your code decides that what you are doing is
| "metaprogramming".
|
| There are other style guides that apply to small subsets of
| google3, some of which forbid certain kinds of template
| constructions (variadic, SFINAE, etc). Maybe you're thinking of
| one of those? I'm not aware of any that forbid all templates.
| kwertyoowiyop wrote:
| L5's usually write code that needs L6's to maintain.
|
| L7's should write code that only needs L4's to maintain.
| cletus wrote:
| I love this so much. This may be one of my favourite comments
| ever.
|
| Can I ask if this has a source? From a particular company?
| Meme? Quote? Paraphrased from something?
| Cthulhu_ wrote:
| I know some versions of it, like Feynman saying that if he
| can't explain it to a freshman, he doesn't understand it.
|
| Likewise, if you write code that only you and someone
| smarter than you can understand, you don't understand it.
|
| That does bring a risk though. If you write simple code
| (e.g. in Go) that does what it does without any trickery, a
| more inexperienced job interviewer may look at it and scoff
| because you're OBVIOUSLY not a very good developer if you
| stick to simple code.
|
| But that's a good litmus test, if they're like that, run
| away. They don't write code to solve a problem, they write
| code to flex and impress themselves, or to provide them
| with job security, or CV boosts.
| joebob42 wrote:
| And l3s and l8s write code that needs l7s to maintain.
| cjfd wrote:
| I don't think picking some C++ dialect is wise. These features
| are there for a reason. Funnily enough a lot of comments that
| respond to this are all of the style 'yes, but nowadays google
| allows this, o and this too, and that as well....' Sure, there
| are things one should not do, like having raw pointers all over
| the place and so on. Such things should be caught in code
| review.
|
| Actually, I totally hate the google style guide. The
| prescriptions about indentation there seem to be optimized to
| make the code as unreadable as possible. An indentation of only
| two is already too little and adding that the opening brace is
| at the end of the line it becomes very difficult to see what
| block ends where.
| emsy wrote:
| Most languages get by with way fewer features and somehow
| people still manage to write programs in them. If you ask 100
| programmers which language has the worst feature creep, 99
| will say C++ and maybe one person APL ;)
| advael wrote:
| Google's "dialect" both makes sense with other things I've
| heard about working for them and makes working for them less
| appealing. The flexible generic and compile-time resolution
| paradigms offered by templates are a lot of the reason it's
| compelling to use C++ over other languages, and while it's a
| totally reasonable language to use for speed if you stick to
| gluing together STL containers/algorithms, making anything
| complicated under those rules sounds very unpleasant.
| mysterydip wrote:
| > It's well known that no one really programs in C++. They
| program in whatever subset of C++ their organization allows,
| their brain can handle or both.
|
| Not to tangent too much but this is exactly how I see
| javascript. I learned it one way, some code I need to use
| learned it a different way, and I have to choose whether to
| attempt to integrate it as-is, or spend the time to "port" it
| to my understanding.
| dataflow wrote:
| It's never been necessary to use delete to make a class
| uncopyable. It was always possible by just declaring (but not
| defining) a private copy constructor/assignment. I'd still do the
| same thing honestly, it's fewer keystrokes and not pointlessly
| backwards-incompatible.
|
| Better yet, for the majority of cases, just inherit from an
| uncopyable class like boost::noncopyable.
| kllrnohj wrote:
| If you declare but don't define a private copy
| constructor/assignment then you hit vague linker errors
| elsewhere that will likely send a user on a chase trying to
| figure out how their include paths don't match their linker
| paths and which library they forgot to link against.
|
| '= delete' makes it exceedingly clear that what they are doing
| is not allowed, rather than "oh god dammit cmake what did I
| miss now..."
|
| Since '= deleted' was added in C++11, there's really not any
| good "backwards-incompatible" arguments to be made about
| avoiding it. C++11 support is extremely broad at this point and
| has been for many, many years now.
| dataflow wrote:
| > If you declare but don't define a private copy
| constructor/assignment then you hit vague linker errors
| elsewhere that will likely send a user on a chase trying to
| figure out how their include paths don't match their linker
| paths and which library they forgot to link against.
|
| I think you missed the importance of _private_. Users of it
| won 't get it to compile for them to get a linker error.
| jcranmer wrote:
| A private copy constructor can still be copied in the class
| itself. If you omit the definition, you still get a linker
| error message, but linker errors are among the most obtuse to
| actually discover. (Where did the copy construction actually
| happen? Who knows! You get, at best, the function that called
| it).
|
| A deleted copy constructor produces better error messages. And
| it's not really backwards-incompatible at this point--deleted
| functions exist in compiler versions old enough to get a
| COVID-19 vaccine.
| dataflow wrote:
| In theory yeah, in practice it's incredibly rare for a class
| to look like it should be copyable to its own maintainer AND
| for the maintainer to try to copy it from its own methods AND
| for a linker error to be obscure for them to figure out when
| they do their first debug build. In fact I'm not sure I've
| _ever_ encountered this situation. Usually it 's pretty
| obvious if your class should be copyable or not from the
| outset so it's not common to make the mistake to begin with.
| metiscus wrote:
| Another worry is that an engineer will see the link error and
| "fix it" instead of getting why it was done and diagnosing
| the misuse of the class.
| dataflow wrote:
| I suppose this is possible for people who never programmed
| C++ before C++11, I haven't really encountered this
| incident to know. The biggest annoyance I've encountered
| isn't a bug - rather, it's the annoying IDE warning that
| the member is unimplemented!
| gpderetta wrote:
| > not pointlessly backwards-incompatible.
|
| if the only reason for using the C++11 standard were the
| =delete syntax, by all means, just use boost::noncopyable. But
| if you are using features from C++11, then there is really no
| reason for not using the new syntax.
| Taywee wrote:
| My original post: Private constructors still match for overload
| resolution, and can cause your compiler to barf when a explicit
| deletion would allow the compiler to find an appropriate legal
| match.
|
| I was wrong. delete leaves it in the overload set. I've been
| using C++ for a decade and I'm still tripping over some of the
| stupider semantics.
| quietbritishjim wrote:
| You edited before I got the chance to make a snarky reply :-)
| For the record, the following snippet will not compile
| because the deleted constructor will be matched during
| overload resolution. class C {
| public: C(int) = delete; C(long) {}
| }; int main() { C x(3);
| }
| bendbro wrote:
| I haven't seen this syntax before and I just knew it would be
| c++.
| admax88qqq wrote:
| The longer I've been away from C++ the happier I've been.
___________________________________________________________________
(page generated 2021-10-18 23:01 UTC)