What =delete means
quuxplusone.github.io
quuxplusone.github.io
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.
What do you mean by this? General template code was forbidden or just templated containers, STL style?
L7’s should write code that only needs L4’s to maintain.
Can I ask if this has a source? From a particular company? Meme? Quote? Paraphrased from something?
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.
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.
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#Template_m...
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.
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.
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.
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.
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.
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.
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.
I don't get it? What's wrong with returning a bool?
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.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.
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.
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.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.
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...
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.
Not anymore.
I interview C++ programmers for my day job, and no, this isn't true at all.
I think you're just repeating internet memes.
The parent comment is merely saying, "It's well known that no one really uses all the features of the C++ programming language productively" in a rhetorically interesting manner.
private : myclass(const &myclass )=delete;
Just to be safe :-)
Edit: NVM, looks like all major compilers will say that the copy ctor is deleted. https://godbolt.org/z/jTd3714Tb
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)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++?
Have fun! Write some cool games!
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.
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.
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.
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?
If it doesn't exist, it should be created and called --C.
(C-- is already taken) https://en.wikipedia.org/wiki/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).
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.
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 Académie Française 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.
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.
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.
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.
Other examples of contextual keywords are 'final' and 'override', which may also occur on function declarations.
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.
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.
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.
you think quux wouldn't be able to find semantic pitfalls in literally every language you would use instead ?
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.
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)
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.
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.)
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.
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...
As a sibling mentioned, it's not even a new syntax as '= 0' has always been there.
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).
but it always was!
Unless we are talking about v1.0 there always such cases when a language is around long enough.
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?
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.
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.
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.
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)
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 :'(
}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.
Better yet, for the majority of cases, just inherit from an uncopyable class like boost::noncopyable.
'= 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.
I think you missed the importance of private. Users of it won't get it to compile for them to get a linker error.
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.
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.
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.
class C {
public:
C(int) = delete;
C(long) {}
};
int main() {
C x(3);
}Yes, that makes RAII basically impossible.
The construct-then-init pattern is so annoying to deal with.
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).
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.