In C++20, we are now able to constrain the auto keyword to only be of a specific type. So if I had some code that looked like the following without any constraints:
auto something(){
return 1;
}
int main(){
const auto x = something();
return x;
}
The variable x here is deduced to be an int. However, with the introduction of C++20, we can now constrain the auto to be a certain type like this:
std::integral auto something(){
return 0;
}
int main(){
const auto x = something();
return x;
}
Doesn't this defeat the purpose of auto here? If I really need a std::integral datatype, couldn't I just omit the auto completely? Am I misunderstanding the use of auto completely?
A constraint on the deduced auto type doesn't mean it needs to be a specific type, it means it needs to be one of a set of types that satisfy the constraint. Note that a constraint and a type are not the same thing, and they're not interchangeable.
e.g. a concept like std::integral constrains the deduced type to be an integral type, such as int or long, but not float, or std::string.
If I really need a std::integral datatype, couldn't I just omit the auto completely?
In principle, I suppose you could, but this would at the minimum lead to parsing difficulties. e.g. in a declaration like
foo f = // ...
is foo a type, or a constraint on the type?
Whereas in the current syntax, we have
foo auto f = // ...
and there's no doubt that foo is a constraint on the type of f.
If I really need a std::integral datatype, couldn't I just omit the auto completely?
No, because std::integral is not a type, it's a concept, a constraint on types (or if you will, a set of types rather than a single type).
Doesn't this defeat the purpose of auto here?
The original purpose of auto in C++11 is telling the compiler: Whatever type you deduce.*
With C++20, auto has an expanded use case - together with a concept, a constraint over types. auto still tells the compiler: Whatever type you deduce - but the deduction must also respect the constraint.
* - ignoring issues like constness, l/rvalue reference etc.
A concept often just move the error earlier in the compilation and makes code a bit more readable (since the concept name is a hint to the reader what you require from a type).
Rephrased:
It is rare you will ever use an auto variable in a way that it will work on every type.
For example:
auto fn(auto x) {
return x++;
}
will not work if you do:
f(std::string("hello"));
because you can not increment std::string, the error is something like:
error: cannot increment value of type 'std::basic_string<char>'
return x++;
If you change the function to:
auto fn(std::integral auto x) {
return x++;
}
You will get an error like:
:6:6: note: candidate template ignored: constraints not
satisfied [with x:auto = std::basic_string] auto
fn(std::integral auto x) {
For a small example this, it does not matter a lot, but for real code often the fn would call fn2 that calls fn3... and you would get the error deep in the std/boost/... implementation file.
So in this way concepts move the error to the site of the first function call.
Related
Why doesn't defining a variable with auto keyword carry the constexpr'ness of the expression used to initialize it?
As an example, consider the following code:
#include <string_view>
constexpr std::string_view f() { return "hello"; }
static constexpr std::string_view g() {
constexpr auto x = f(); // (*)
return x.substr(1, 3);
}
int foo() { return g().length(); }
With GCC 10.2 and --std=c++20 -fsanitize=undefined -O3, this compiles into:
foo():
mov eax, 3
ret
But if we remove the constexpr on line (*), we would get a 27-line program with a bunch of pointers, a long string constant etc.
Notes:
I marked this question C++20, but I have no reason to believe this behavior is different from C++11's.
This question is not about the example, it is about the general behavior of auto w.r.t. constexprness. The example simply shows that that GCC does not treat x as constexpr if we don't explicitly tell it to.
auto is intended to enable type deduction, not a replacement for "everything useful you would have typed here". constexpr is not a part of an expression's type, and is thus ignored by auto (as opposed to const and volatile, which are part of an expression's type, and are deduced).
But if we remove the constexpr on line (*), we would get a 27-line program with a bunch of pointers, a long string constant etc.
That is a choice for your compiler. It has 100% of the information it needs to make that code go away. The fact that it didn't is not the C++ standard's concern.
This is a "quality of implementation" issue, not a standardization issue. If an implementation won't run as much of your code at compile-time as you desire, you can complain to them about it.
Remember: constexpr isn't meant to be a runtime optimization per-se. It's meant to allow you to write things that you otherwise couldn't write. Like std::get<g()>(some_tuple) or whatever. That code has to run at compile-time, since it's being used in a template parameter.
I'm not asking about some kind of deep deduction, only about the case of the function explicitly being constexpr.
Let's forget for a moment that auto is for type deduction and constexpr is not part of the type system. Let's focus instead on what if auto was supposed to deduce constexpr anyway. So what you want is for auto to only deduce constexpr if <expr> is specifically a function that is designated constexpr.
So let's look at some code:
auto x = constexpr_func();
auto y = constexpr_func() + 5;
auto z = constexpr_func() + constexpr_func();
auto w = constexpr_func_2() + constexpr_func_2();
Which of these variables are constexpr? If what you want is what we had, then x would be constexpr, but y would not. I personally would find this both surprising and annoying.
Worse, if we assume constexpr_func() returns an int, then z is also not constexpr. But if constexpr_func_2() returns a user-defined literal type that has a constexpr operator+, then w would be constexpr.
Isn't that all very weird? So I highly suspect that this is not what you really want.
What you really want is for auto x = <expr>; to deduce constexpr if constexpr auto x = <expr>; would be valid.
But really, that goes back to the original point. If you make a variable constexpr, that should mean you want it to be used in a place where being constexpr is required by some process. Given that fact, deducing constexpr makes no sense, because you should need it to be constexpr lest you get a compile error.
Is there any case, where we must use trailing return type, because the problem cannot be phrased in the old way?
auto fn() -> int; can be easily transformed to the old way: int fn();.
I wonder, is there an example, where this transformation is not possible. The most straighforward example, when we refer to function parameters in the return type, seems to be solvable by using declval.
Note: don't consider lambdas here, where we must use trailing return type.
In a trailing return type, you're allowed to apply decltype to this (see this question).
With the old syntax, you'd have to spell the class name manually... which you can't do if the class is unnamed!
(Or if the member function is generated with a macro, so the class name isn't known.)
struct
{
auto foo() -> decltype(this)
{
return this;
}
/*
decltype(this) foo() // error: invalid use of 'this' at top level
{
return this;
}
*/
} x;
I admit that this is a slightly unrealistic example, and you can easily work around it by naming the class, but I couldn't think of anything else.
One bizzare example I can think of, which needs some prerequisites.
Consider a function which cannot use auto return type deduction (e.g. it has multiple return values which cannot be deduced to the same type) and uses generic function from C++ concepts. Then you don't have a type to use for std::declval and auto deduction won't work:
auto foo(auto x)
// -> decltype(x) // comment this out to fix
{
if(x > 0) return x;
return -1; // requires int to be implicite castable to type of x
}
Demo
I see decltype(x) used inside macros where x is a variable name because the type of the object isn't known inside macros.
For example:
decltype(x) y = expr;
I could just have easily use auto instead of decltype. So what are those situations where decltype is needed for a variable type declaration instead of auto?
decltype becomes handy when you need to return some unknown type, which is evaluated during compilation:
template<class A, class B>
void MultiplyAB(A a, B b, decltype(a*b)& output)
{
output = a * b;
}
Additionally, if you don't like the way the output is handled by a reference, then you can also use the late-specified return type (and also use the decltype):
template<class A, class B>
auto MultiplyAB(A a, B b) -> decltype(a*b)
{
return a * b;
}
All of this, and more, is described by B. Stroustrup in the C++ FAQ.
You should use it when the required type of y is:
different (or potentially different) from the type of expr. If it was the same then auto would be more concise.
similarly for auto & or other modifications of the type of expr that auto can express.
and one of the following:
dependent on something in the surrounding code (i.e. not always the same type) and difficult to write using type traits or similar. This will tend to happen in template code. There might be a type trait that you can use to get the required type from the template parameters, but then again there might not so a use of decltype would save you defining one.
always the same type, (or dependent on template parameters in a way that is easy to express using existing type traits or similar) but the type is very long-winded to write and there is a much shorter and clear expression you can use instead.
So for example replacing std::iterator_traits<RandomAccessIterator>::value_type with decltype(*it) might well be a win, although auto does often handle such cases.
Subjective judgements enter at the point of "what is difficult", "what is long-winded" and "what is clear", but the rules of procedure can be the same regardless of how you make those judgements in specific cases.
When you want y to always have whatever the declared type of x is.
In the context of your question,
You should use decltype when you want a new variable with precisely the same type as the original variable.
You should use auto when you want to assign the value of some expression to a new variable and you want or need its type to be deduced.
decltype(x) y always declares y with precisely the same type as the type x was declared with. In particular:
If x has type const int then y will have type const int.
If x has type int[100] then y will have type int[100].
If x has type int f(int) then y will have type int f(int). Yes, this actually declares another function with the same type as the original.
If x has type int& then y will have type int&; and if x has type int&& then y will have type int&&.
auto y = x will declare y with the following types, when x has the following types:
If x has type const int, then y will have type int. That is, auto strips top-level cv-qualifiers.
If x has type int[100], then y will have type int*. That is, auto performs array to pointer conversion. [1]
If x has type int f(int), then y will have type int (*)(int). That is, auto performs function to function pointer conversion. [2]
Finally, if x has type int& or int&&, then y will have type int. That is, auto removes references.
[1] You can't use decltype here because you can't copy-initialize an array.
[2] You can't use decltype here because you can't initialize a function.
[3] The reason why auto strips references is that C++ has no expressions of reference type! Once initialized, the "reference ness" of a reference becomes invisible.
Note that decltype also does something entirely different when its argument is not an id-expression, which I won't get into here.
Whenever your variable type isn't related to the expression being evaluated.
E.g:
struct Bar
{
Bar(int) {} // implicitly constructable
}
struct Bar2
{
Bar2(int) {} // implicitly constructable
}
struct Foo
{
static Bar var;
}
struct Foo2
{
static Bar2 var;
}
template <typename T>
void dummy()
{
decltype(T::var) myVar = 42;
}
dummy<Foo>(); // myVar is of type Bar1
dummy<Foo2>(); // myVar is of type Bar2
auto myAutoVar = 42; // type is int
Of course this is just one use case, there are many more out there.
decltype is significantly more versatile that auto and can always be used in place of it. Therefore I think it's pretty safe to say that decltype should only be used in cases where it's completely necessary, so if auto produces the wrong result you should use decltype. Also you can't as of yet use auto in return types and parameters, so you can use decltype there as well. C++14 will significantly increase the potential uses of auto and I would guess c++17 will go further. So the situations to use decltype will only be when you need to change the resulting type of expr
Another thing to consider is that decltype isn't really necessary unless you're writing library code, auto is nice for everyday programming if you want to make your code more concise, it's up for debate wether using as much auto as possible is good, but it's virtually necessary when working with unutterable types like lambdas.
Assume I have the following exemplary function:
template <typename Fn> auto call(Fn fn) -> decltype(fn()) {
return fn();
}
The important thing about this function is that its return type depends on its template parameter, which can be inferred. So ultimately, the return type depends on how the function is called.
Now, we also have a test class:
struct X {
int u;
auto test() -> decltype(call([this]() -> double {this->u = 5; return 7.4;})) {
return call([this]() -> double {this->u = 5; return 7.4;});
}
};
as you can see, X::test calls call, returning the same return value. In this case, the return type is trivially given as double, but let's assume for a bit we didn't know what call does and that the lambda has a more complicated return type.
If we try to compile this, the compiler will complain, because we're using this at the top level (not in a scope that would allow an expression):
error: lambda-expression in unevaluated context
error: invalid use of ‘this’ at top level
However, I have to use the capture of the lambda which I pass to call in order to get call's return type right. How would you suggest to get around this, while still leaving the lambda?
Note: Of course I could move the lambda to be an operator() of some helper type, which I instantiate with a copy of the this pointer, but I'd like to avoid that boilerplate.
I think the real error to be concerned about is "lambda-expression in unevaluated context". You can't use a lambda in an unevaluated context because every lambda expression has a unique type. That is, if decltype([]{}) were allowed it would deduce a different type than []{} in some other context. I.e. decltype([]{}) fn = []{}; wouldn't work.
Unless you want to just explicitly write the return type rather than have it deduced, I don't think you have any choice but to create a real type that you can use in the contexts you need, with whatever boilerplate that entails.
Although if changing test to not be a member function is acceptable then you could use the fact that lambda's can deduce their return type by omitting it if the body is only a single return statement:
template <typename Fn> auto call(Fn fn) -> decltype(fn()) {
return fn();
}
struct X {
int u;
};
int main() {
auto test = [](X *x) { return call([x]() -> double {x->u = 5; return 7.4; });};
X x;
test(&x);
}
It would be nice if the trailing return type syntax for functions had the same property. I'm not sure why it doesn't.
It seems to be a made up (construed, artificial) question, since
If you get the lambda from somewhere else, then it's named and no problem binding this.
If you're not getting the lambda from somewhere else, then you know the result type.
In short, as the problem is currently stated (as I'm writing this answer) there's no problem except one imposed by your own will.
But if you insist on that, well, just pass this as an argument instead of binding it via the lambda definition. Then for the call to call, bind the argument. But, perhaps needless to say, since that only solves a made-up problem it's a real Rube Goldberg construction, a decent into over-flowering needless complexity that doesn't solve anything real outside its own intricacies.
What was the original real problem, if any?
You shouldn't always copy-and-paste function body to decltype. The point of introducing late-specified return type was that you'll be able to somehow infer correct return type from arguments.
e.g. auto f(T x) -> decltype(g(x)) { return h(), g(x); }, not -> decltype(h(), g(x))
So, in your case, double test() is enough, because we know behavior of call and we know return type of lambda function we pass to it.
In more complex case, we should reduce code inside decltype, by using knowledge about call and other stuff.
From all the material I used to learn C++, auto has always been a weird storage duration specifier that didn't serve any purpose. But just recently, I encountered code that used it as a type name in and of itself. Out of curiosity I tried it, and it assumes the type of whatever I happen to assign to it!
Suddenly STL iterators and, well, anything at all that uses templates is 10 fold easier to write. It feels like I'm using a 'fun' language like Python.
Where has this keyword been my whole life? Will you dash my dreams by saying it's exclusive to visual studio or not portable?
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.
It's just taking a generally useless keyword and giving it a new, better functionality. It's standard in C++11, and most C++ compilers with even some C++11 support will support it.
For variables, specifies that the type of the variable that is being declared will be automatically deduced from its initializer. For functions, specifies that the return type is a trailing return type or will be deduced from its return statements (since C++14).
Syntax
auto variable initializer (1) (since C++11)
auto function -> return type (2) (since C++11)
auto function (3) (since C++14)
decltype(auto) variable initializer (4) (since C++14)
decltype(auto) function (5) (since C++14)
auto :: (6) (concepts TS)
cv(optional) auto ref(optional) parameter (7) (since C++14)
Explanation
When declaring variables in block scope, in namespace scope, in initialization statements of for loops, etc., the keyword auto may be used as the type specifier.
Once the type of the initializer has been determined, the compiler determines the type that will replace the keyword auto using the rules for template argument deduction from a function call (see template argument deduction#Other contexts for details). The keyword auto may be accompanied by modifiers, such as const or &, which will participate in the type deduction. For example, given const auto& i = expr;, the type of i is exactly the type of the argument u in an imaginary template template<class U> void f(const U& u) if the function call f(expr) was compiled. Therefore, auto&& may be deduced either as an lvalue reference or rvalue reference according to the initializer, which is used in range-based for loop.
If auto is used to declare multiple variables, the deduced types must match. For example, the declaration auto i = 0, d = 0.0; is ill-formed, while the declaration auto i = 0, *p = &i; is well-formed and the auto is deduced as int.
In a function declaration that uses the trailing return type syntax, the keyword auto does not perform automatic type detection. It only serves as a part of the syntax.
In a function declaration that does not use the trailing return type syntax, the keyword auto indicates that the return type will be deduced from the operand of its return statement using the rules for template argument deduction.
If the declared type of the variable is decltype(auto), the keyword auto is replaced with the expression (or expression list) of its initializer, and the actual type is deduced using the rules for decltype.
If the return type of the function is declared decltype(auto), the keyword auto is replaced with the operand of its return statement, and the actual return type is deduced using the rules for decltype.
A nested-name-specifier of the form auto:: is a placeholder that is replaced by a class or enumeration type following the rules for constrained type placeholder deduction.
A parameter declaration in a lambda expression. (since C++14) A function parameter declaration. (concepts TS)
Notes
Until C++11, auto had the semantic of a storage duration specifier.
Mixing auto variables and functions in one declaration, as in auto f() -> int, i = 0; is not allowed.
For more info : http://en.cppreference.com/w/cpp/language/auto
This functionality hasn't been there your whole life. It's been supported in Visual Studio since the 2010 version. It's a new C++11 feature, so it's not exclusive to Visual Studio and is/will be portable. Most compilers support it already.
The auto keyword is an important and frequently used keyword for C ++.When initializing a variable, auto keyword is used for type inference(also called type deduction).
There are 3 different rules regarding the auto keyword.
First Rule
auto x = expr; ----> No pointer or reference, only variable name. In this case, const and reference are ignored.
int y = 10;
int& r = y;
auto x = r; // The type of variable x is int. (Reference Ignored)
const int y = 10;
auto x = y; // The type of variable x is int. (Const Ignored)
int y = 10;
const int& r = y;
auto x = r; // The type of variable x is int. (Both const and reference Ignored)
const int a[10] = {};
auto x = a; // x is const int *. (Array to pointer conversion)
Note : When the name defined by auto is given a value with the name of a function,
the type inference will be done as a function pointer.
Second Rule
auto& y = expr; or auto* y = expr; ----> Reference or pointer after auto keyword.
Warning : const is not ignored in this rule !!! .
int y = 10;
auto& x = y; // The type of variable x is int&.
Warning : In this rule, array to pointer conversion (array decay) does not occur !!!.
auto& x = "hello"; // The type of variable x is const char [6].
static int x = 10;
auto y = x; // The variable y is not static.Because the static keyword is not a type. specifier
// The type of variable x is int.
Third Rule
auto&& z = expr; ----> This is not a Rvalue reference.
Warning : If the type inference is in question and the && token is used, the names
introduced like this are called "Forwarding Reference" (also called Universal Reference).
auto&& r1 = x; // The type of variable r1 is int&.Because x is Lvalue expression.
auto&& r2 = x+y; // The type of variable r2 is int&&.Because x+y is PRvalue expression.
The auto keyword specifies that the type of the variable that is being declared will be automatically deducted from its initializer. In case of functions, if their return type is auto then that will be evaluated by return type expression at runtime.
It can be very useful when we have to use the iterator. For e.g. for below code we can simply use the "auto" instead of writing the whole iterator syntax .
int main()
{
// Initialize set
set<int> s;
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(5);
s.insert(3);
// iterator pointing to
// position where 2 is
auto pos = s.find(3);
// prints the set elements
cout << "The set elements after 3 are: ";
for (auto it = pos; it != s.end(); it++)
cout << *it << " ";
return 0;
}
This is how we can use "auto" keyword
It's not going anywhere ... it's a new standard C++ feature in the implementation of C++11. That being said, while it's a wonderful tool for simplifying object declarations as well as cleaning up the syntax for certain call-paradigms (i.e., range-based for-loops), don't over-use/abuse it :-)
It's Magic is it's ability to reduce having to write code for every Variable Type passed into specific functions. Consider a Python similar print() function in it's C base.
#include <iostream>
#include <string>
#include <array>
using namespace std;
void print(auto arg) {
cout<<arg<<" ";
}
int main()
{
string f = "String";//tok assigned
int x = 998;
double a = 4.785;
string b = "C++ Auto !";
//In an opt-code ASCII token stream would be iterated from tok's as:
print(a);
print(b);
print(x);
print(f);
}