C++ auto keyword. Why is it magic?
auto
was a keyword that C++ "inherited" from C that had been there nearly forever, but virtually never used because there were only two possible conditions: either it wasn't allowed, or else it was assumed by default.
The use of auto
to mean a deduced type was new with C++11.
At the same time, auto x = initializer
deduces the type of x
from the type of initializer
the same way as template type deduction works for function templates. Consider a function template like this:
template<class T>
int whatever(T t) {
// point A
};
At point A, a type has been assigned to T
based on the value passed for the parameter to whatever
. When you do auto x = initializer;
, the same type deduction is used to determine the type for x
from the type of initializer
that's used to initialize it.
This means that most of the type deduction mechanics a compiler needs to implement auto
were already present and used for templates on any compiler that even sort of attempted to implement C++98/03. As such, adding support for auto
was apparently fairly easy for essentially all the compiler teams--it was added quite quickly, and there seem to have been few bugs related to it either.
When this answer was originally written (in 2011, before the ink was dry on the C++ 11 standard) auto
was already quite portable. Nowadays, it's thoroughly portable among all the mainstream compilers. The only obvious reasons to avoid it would be if you need to write code that's compatible with a C compiler, or you have a specific need to target some niche compiler that you know doesn't support it (e.g., a few people still write code for MS-DOS using compilers from Borland, Watcom, etc., that haven't seen significant upgrades in decades). If you're using a reasonably current version of any of the mainstream compilers, there's no reason to avoid it at all though.
More recent revisions of the standard have added a few new places that auto
can be used. Starting with C++14, you can use auto
for the type of a parameter to a lambda:
[](auto s) { return s + 1; }
This does essentially the same thing as the example above--even though it doesn't explicitly use template
syntax, this is basically a template that deduces the type of the parameter, and instantiates the template over that type.
That was convenient and useful enough that in C++20, the same capability was added for normal functions, not just lambdas.
But, just as before all of this really comes down to using the same basic type deduction mechanism as we've had for function templates since C++98. auto
allows that to be used in more places, and more conveniently, but the underlying heavy lifting remains the same.
Confusion about auto keyword in C++
auto
is not a storage class. It used to be, before C++11. But it was completely useless, so the keyword was re-purposed to allow automatic type inference. So when you say:
auto int ret = foo();
You are basically declaring the object to have 2 types (or possibly the same type twice), and that is an error. And when you say:
auto ret = foo();
The type of ret
is determined by whatever the function foo
returns, which is int*
in this case.
Why do I need to explicitly write the 'auto' keyword?
Dropping the explicit auto
would break the language:
e.g.
int main()
{
int n;
{
auto n = 0; // this shadows the outer n.
}
}
where you can see that dropping the auto
would not shadow the outer n
.
Is C++11 auto keyword exactly defined for all cases? Or: how does auto know what I intend?
What will
a
be?
int
, since that's the type of 4
.
Will
x
be of type*B
or of type*A
?
B*
, since that's the type of new B()
.
Is there an exact list of the
auto
behaviour?
Usually, it's the type of the initialiser; unless that's a reference type, in which case it's the underlying object type. There are a few other wrinkles for unusual types like arrays, as mentioned in the comments.
will it always be the exact same type (with the exact same bit length!) on each compiler and each architecture?
In most cases, the initialiser has a well-defined type, and that determines the type deduced by auto
.
If the initialiser is an integer literal, then the type might depend on the platform; for example, 1000000
might be int
on a 32-bit platform, but long
on a 16-bit platform.
auto keyword strange behavior in C++11
The auto
storage class specifier is not "useless and deprecated in C++11," it has been removed entirely. The auto
keyword is no longer a storage class specifier and cannot be used as one.
In C++11, auto
is a simple type specifier.
Does auto keyword always evaluates floating point value as double?
The issue is that your code is using ::sin
instead of std::sin
(and the same for cos
). That is, you’re using the sin
function found in the global namespace.
std::sin
is overloaded for float
. But ::sin
isn’t, and it always returns a double
(because ::sin
is the legacy C function, and C doesn’t have function overloading).
Use std::sin
and std::cos
in your code to fix the issue.
where auto keyword can't determine the type in C++
"Can't" is a strong word. After all, lambda parameters can use auto
(in C++14). It's not so much "can't" as "doesn't". And perhaps "won't".
The question ultimately comes down to this: what does this actually do?
void foo(auto x)
{
std::cout << x << std::endl;
}
auto
deduction is ultimately based on providing an initializing expression, which is used to deduce the actual type. This declaration contains no initializing expression.
C++ is a statically typed language; that means the compiler must be able to determine the type of every expression and object at compile time. From just the above, the compiler can deduce nothing.
So how can the compiler know that std::cout << x
is legal C++ syntax? It can only tell that if there is an operator<<
overload that takes std::cout
and x
. It can't figure out what is being called without first knowing the type of x
. Which it doesn't; it can be different for different invocations of the function.
However, there is one C++ construct where the above makes sense: a template. This is exactly what using auto
in lambda parameters does; it implicitly transforms the function into a template function. So [](auto x) {return x;}
effectively becomes an operator something like this:
template<typename T>
auto operator()(T x) {return x;}
However, this conversion doesn't just fall out of having auto
as a deduction syntax. It has to be something the standard is explicitly written to require. And, with the exception of C++14 generic lambdas, it doesn't.
Concepts TS includes this facility. However, this is merely an extension of the ability to use concepts in function parameter lists at all. That is, they already have a way to make a function implicitly create a template function, so they just added auto
as essentially a case of "a concept that accepts any type".
But this was explicitly excluded from the C++20 version of concepts. The general reason for this exclusion is that, up until this point, template functions could be detected because they always had to have some special syntax. Namely, the inducer template<args>
. With the Concepts TS version, there is concern that people will write template functions without realizing it.
And template functions behave differently from non-template functions. A template function is a family of functions, so you can't get a pointer to the family itself. You'd have to explicitly instantiate the template to get a pointer to that particular function.
How much is too much with C++11 auto keyword?
I think that one should use the auto
keyword whenever it's hard to say how to write the type at first sight, but the type of the right hand side of an expression is obvious. For example, using:
my_multi_type::nth_index<2>::type::key_type::composite_key_type::
key_extractor_tuple::tail_type::head_type::result_type
to get the composite key type in boost::multi_index
, even though you know that it is int
. You can't just write int
because it could be changed in the future. I would write auto
in this case.
So if the auto
keyword improves readability in a particular case then use it. You can write auto
when it is obvious to the reader what type auto
represents.
Here are some examples:
auto foo = std::make_shared<Foo>(); // obvious
auto foo = bla(); // unclear. don't know which type `foo` has
const size_t max_size = 100;
for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors
// since max_size is unsigned
std::vector<some_class> v;
for ( auto it = v.begin(); it != v.end(); ++it )
// ok, since I know that `it` has an iterator type
// (don't really care which one in this context)
Related Topics
How to Assert If a Std::Mutex Is Locked
Linux C++ Error: Undefined Reference to 'Dlopen'
Why Is a NaïVe C++ Matrix Multiplication 100 Times Slower Than Blas
How to Monitor a Folder with All Subfolders and Files Inside
C++ Fast Screenshots in Linux for Use with Opencv
Illegal Token on Right Side of ::
Boost Asio Single Threaded Performance
Why Can't I Add a Qgridlayout to a Qmainwindow
Why Isn't Malloc Filling Up Memory
Create Window Without Title Bar
Send Mail Using Smtp in C++ on Linux
Mpirun: Unrecognized Argument Mca
Using Qsocketnotifier to Select on a Char Device
Is There a Compiler Hint for Gcc to Force Branch Prediction to Always Go a Certain Way
How Std::Bind Works with Member Functions