Don’t use C++ auto? Restricting auto is not the best decision
swdevmastery.com
swdevmastery.com
Compilers know types better than humans.
Whoever bans it (whithout a very, very compelling reason like "some of our code has to compile in an old compiler") is being a luddite at best.
This should have been in C++ from its inception to be honest, to avoid blah_type<with_this> something = new blah_type<with_this>() repetition.
It was, but the backslash was even greater back then, so Bjarne removed it from C with Classes.
There are a few presentations/interviews where he mentions it.
Before C++11 auto meant it was not static, extern or register, and in fact every single variable was already implicitly auto if no specifier from the previous list was applied.
int n = 10;
In that line, n was auto. Now it is not auto, only int.Bjarne used another keyword in C with Classes, removed it due to backslash, and the commite decided to re-purpose auto for the same purpose in C++11.
No big deal, the meaning of static is also context dependent, and no one is fighting against it like it happens with the new meaning for auto.
Say we have a function, GetInt64(), which...returns an int64. x := GetInt64() both declares x to be an int64 and places the resulting value of the right hand side into x. If x was already to declared to be an int64, E.G.:
var x int64
x := GetInt64()
Then the compiler complain that x is not new - it has already been declared. In this case you have to omit the colon to remove the fancy "auto" semantics.
var x int64
x = GetInt64()
For example
a := "hello"
or a := 0
or a := []byte{0, 1}x = 5 <-- a boolean statement, true if x equ 5
x := 5 <-- an assignment of the value 5 to the var x.
TBH, it's only like '=' vs '==' in C like languages. But it's easier to spot errors, as Pascal can never have a valid statement where the two are used in the wrong way.
Type inference for this is about the only way to make "enterprisey" C#/Java code palatable.
The only problem is if you're a .NET shop that is a little crusty, and people start having PTSD flashbacks and conflate the C# type inferred "var" with the old VisualBasic monstrosity Variant type.
Yes:
auto foo = new Foo(bar);
Maybe: auto foo = FooFactory::newFromBar(bar);
No: auto foo = bar(baz);As mentioned in another comment, not being able to name or even easily refer to a type without auto is very common in generic code.
auto itemId = item->id(); // Could be an int, could be a string...
processSomeItem(itemId);1. "item->id()" is designed to return a pointer.
2. Consumer of the API writes their code to take a copy of the pointer and passes it along to "processSomeItem". It is cheap to make a copy of a pointer
3. The API is changed and it now returns a reference.
4. The assignment to "itemId" now makes an expensive copy of the object behind the reference causing inefficiency bug.
5. "processSomeItem()" API can handle the copy and the code compiles and runs without the problem being noticed.
std::function<void()> = [] { ... };
Vs: auto = [] { ... }
In the first case you are doing type erasure, which adds quite few penalties. Even in other cases, the type you typed might be convertible from the actual thing that is returned, causing extra conversions. If you always use "auto" the chances you use the right type and do less conversions is way higher.[1] https://github.com/mhogomchungu/tasks
[2] https://github.com/mhogomchungu/tasks/blob/a1512a1b5e0392a06...
template< typename T,typename ... Args >
future<T>& run( std::function< T( Args ... ) > function,Args ... args )
{
return Task::run<T>( std::bind( std::move( function ),std::move( args ) ... ) ) ;
}
It should look like this for that example to work without the type erasure (haven't tried, there might be typos): template< typename Fn,typename ... Args >
future<std::result_of_t(Fn(Args...))>& run( Fn&&, Args&& ... args )
{
return Task::run<std::result_of_t(Fn(Args...))>( std::bind( std::forward<Fn>( function ),std::forward<Args>( args ) ... ) ) ;
}
This version is also more efficient, since otherwise you are type erasing twice.[1] https://github.com/mhogomchungu/tasks/blob/4210a8fad57958fad...
Thank you very much.
Also, passing stuff by reference does not make a copy.
That said, there are awful compiler inferred types that C# doesn't have, so `auto` has a much wider application than `var` does.
auto foo = std::make_unique<Foo>(25.3, true);
Duck typing works fine.
void mutate(Foo &foo);
...
for(auto foo : manyFoos) mutate(foo);
If manyFoos is Foo[], then the auto type is Foo, rather than Foo& and you're mutating a copy. The goal is to not get screwed by the type inference, so unless you can defend why it's obvious that the type is what it is, don't use auto. An auto that you need to ask the IDE for it's concrete type is a strong code smell. auto& foo = bar(baz);(The point about the 'interesting' part is to permit stuff like std::unqiue_ptr<T> F<T>() and the like.)
std::unordered_map<SomeTemplatedType<A, B>, SomeOtherTemplatedType<C, D>> map = foo();
std::unordered_map<SomeTemplatedType<A, B>, SomeOtherTemplatedType<C, D>>::iterator it = map.begin();
is much less readable than std::unordered_map<SomeTemplatedType<A, B>, SomeOtherTemplatedType<C, D>> map = foo();
auto it = map.begin();``` template<typename T> void f(const T& t) { auto val = t.some_method(); } ```
and here it cannot work because T can be an arbitrary type and there's simply not enough info for intellisense to work and suggest members on val.
It's very useful while coding because you don't have to wait until compilation time to see an error, and it's a feature I wish VS would implement.
That sounds more like your problem with Visual Studio.
Are you using a plugin like Visual Assist? It's a great plugin, but it does have issues where it behaves as you describe.
abstract type Number end
abstract type Real <: Number end
abstract type AbstractFloat <: Real end
abstract type Integer <: Real end
abstract type Signed <: Integer end
abstract type Unsigned <: Integer end
... then I can make a function that takes any value as long as it's a subtype of Number, which implies I can do addition and subtraction and all the other things all numbers can do. So I still have the type safety of not ending up with an object where I want a number, but it is still extremely easy to write generics in Julia[0].Can you do something like that in C++? So like:
auto <: SomeBaseClass
Meaning auto would work just like it normally does, except that if you assign something that is not derived from SomeBaseClass the compiler complains, giving you some extra safety checks when composing code with auto all over the place.Concepts are in the process of standardization and are expected to be part of C++20.
This is nothing to do with inheritance though in C++. Concepts are type constraints and don't require any particular inheritance hierarchy which is a much better way to handle things than using inheritance for this purpose.
Some people don't want to learn how it works, and have been bitten by unexpected results. Like nearly everything in c++, there are some odd corner cases.
However, auto is a big net win. You should understand it, and you should use it.
Which is a fair summary of the linked article.
I was expecting some style guide rules of thumb.
Scott Meyers does a good job of explaining “auto” in Effective Modern C++.
No, I'm thinking "Holy cow! He used so many words to explain such simple behavior!"
The initializer list rule seems like a bad idea, at least.
auto float x;
when compiled with a C++ compiler, produces an error message.I don't know; "auto" to me means "automatically deduced". While "var" sounds like it should apply only to variables, and auto to types (including return types, argument types etc).
The reality is it could have been called "floog" -- after a while I don't even think of the English meaning of "if" when I write "if (foo)...", much less "for(...", and non-english speakers simply learn the keywords by rote, much as non-Italian speakers learn western musical notation by rote (or do you silently translate "fortissimo" in your head as you play?)
template<typename Collection>
void foo(const Collection &c) {
auto x = c.find("foo");
...
}
is x an iterator (Collection is std::map) or a number (Collection is std::string)? Here it is better to write out the iterator type if that's what you mean.auto can also be downright misleading:
template<typename T>
void foo(std::vector<T> &c) {
auto first = c[0];
...
}
`first` has type T except when T is bool. Gross. Better to write out the type T.auto can simplify many cases, but please don't take Sutter's "almost always auto" suggestion. Keep your future code readers in mind and be aware of its pitfalls.
The reader would be able to infer this from context (as is the compiler, so it's still type-safe).
> `first` has type T except when T is bool. Gross. Better to write out the type T.
That's a preexisting problem with `vector<bool>` much more than it is with `auto`.
I'm quite happy to ignore the type in most cases (especially in templates) when I see a line like auto dest = make_data_sink(....blah blah....);
At that point I am unlikely to even worry about the type.
My poor monkey brain couldn't cope with the amount of negation, and thought it's a criticism of "auto".
I do find myself writing code with auto, then when I revisit it at some point, replace it with the explicit type for readability. But that's only for a small percentage of the cases where I use auto, so overall it's a net plus.
That applies to many C++ features, not just auto.
Maybe the problem with C++ is that it's not a language that you can use well without developing a sense of good taste - knowing when to use certain things, and when not to. (And then you have to interface with code written by someone else who didn't have any taste, and just used features without regard to whether they made things better or worse...)
template<typename T> class TD;
TD<decltype(YourTemplatedAutoClass)> td;
Compiler emits deducted class because TD is declared but not defined and can't be used.
Didn't even want to use your normal account? Way to stand behind your inflammatory comments.
It's weird how people can't fathom that we're not all using their favorite language for real-time, safety-critical systems. Don't you think it's with good reason? That, in billion-dollar projects, all options have been considered? Everyone is using C and C++ for this purpose. From BAE to JPL.
Hiring process, politics and developer salaries also play a big role.
Go ask the Rust or D guys if their language and tools are ready to replace C++ for the F-35 project.
Regarding games, most AAA game engines are hybrid, with C++, C and Assembly only being used for the graphics and audio processing core.
Everything else on the engine is a mixture of D, C#, Blueprints, Lua, Python, Flash, Lisp, in-house scripting language, depending on the studio.
I never meant to imply that games are comprised of only C++. Of course, you are right: a game and its tooling consist of several languages. Unreal Engine's build system is C#--but as you say, the core systems which makes sure everything's executed in time, rendering, physics, audio, are C, C++ with a bit of assembly.
And look at what Unity has to do with IL2CPP to increase performance. Unity has terrible performance because of Mono, but their work on the compiler and job system should start to pay off. In the case of Unreal Engine, you gain a lot of performance by using C++ instead of Blueprints. Even so, Blueprints already compile to bytecode and is ran in a VM and from there they call native C++ functions, they're not interpreted as is. Not that that has much relevance to what we're talking about, perhaps, but it does show that making a system like Blueprints is a significant performance compromise you always try to make up for. Epic recommends you use Blueprint Nativization (generating native code from your BPs) for increased performance, and just use C++ outright for heavy lifting parts. It's not without reason that Epic decided to drop UnrealScript and just use C++ as their scripting language, as well.
This comment came to be about Unreal Engine. I don't want to detract from Blueprints though; they're great at what they do. And not to be thought of as just visual scripting for people who can't program or something for only your artists/designers to use. A mistaken thinking many seem to share. It's best to use a combination of the two for easy gameplay scripting and iteration. Just write C++ and expose it to your BPs.