In the following code:
int foo() {
int a = 5;
auto l = [&r = std::move(std::as_const(a))] { return r; };
return l();
}
clang compiles just fine
gcc produces error.
error: cannot capture 'std::move<const int&>((* & std::as_const<int>(a)))' by reference
I need community help to argue about this case from C++ standard point of view: who is correct in C++20 and why exactly.
From [expr.prim.lambda.capture] not clear to me if this clause:
An init-capture without ellipsis behaves as if it declares and explicitly captures a variable of the form "auto init-capture"
means that clang is right? If yes then why removing as_const makes this an error for both?
code example on godbolt
Seems to be a GCC bug. The init-capture should behave as if declaring a corresponding variable with auto prefixed and which is then captured (exactly as in your quote from the standard):
auto &r = std::move(std::as_const(a));
This would deduce auto to const int so that the variable has type const int& and initialization would be well-formed because the initializer is a rvalue of type const int which a const lvalue reference can bind to directly.
However std::move has no effect here and the result would be identical without it. (std::move on a const type basically never makes sense.)
It fails without as_const because then auto will be deduced to just int so that the variable has type int&. You are then trying to initialize it from an rvalue (the result of std::move) which is not allowed when initializing non-const lvalue references.
Related
Consider the following code:
#include <type_traits>
int main() {
const int& p = 42;
auto v1 = decltype(p){};
static_assert(std::is_same_v<decltype(v1), int>);
decltype(p) v2{};
static_assert(std::is_same_v<decltype(v2), const int&>);
// auto v3 = X(const int&)X {};
}
Type of v1 is deduced as int. At the same time type of v2 is expectedly deduced as const int&. I think the first step for v1 could be treated as adding one more type alias using T = decltype(p); and then auto v4 = T{};. How this expression (decltype(p){} or T{}) is treated by compiler? I know that {} part is for instantiation, but how the resulting type of v1 is not a reference type?
Yet another question: is there a way to declare v3 variable of the same type as v1 using explicitly noted type const int& (instead of decltype(p))?
Any links to standard would be appreciated.
(To the downvoter(s): if you downvoted because you think quoting Scott Meyers is not equivalent to quoting the standard, oh well...)
As you can read from Effective Modern C++ (augmented with the part of the errata that you can reach by searching for Case 2: at that link, and that just makes the following read simpler, but it's not essential to the question):
If ParamType is a non-reference [...] if expr's type is a reference, ignore the reference part. If [...] expr is const, ingore that too. If it's volatile, also ignore that.
where param is the declaration specifier, which in your case is just auto, i.e. a non-reference.
In other words, you're creating v1 via plain auto (not auto&), i.e. by copy, so it does not matter whether you are initializing it with an entity which is reference or not, or even with const or not (or volatile or not, fwiw), because you're copying it.
Think about the simpler case,
int i = 3;
int& p = i;
auto v1 = p;
as far as v1 is concerned, it's really not important whether it is initalized with one (i) or the other (p) name by which the same entity is known, because it will get a copy of whatever value that entity has.
auto type deduction works just like template type deduction (except for a difference in how they deal with braced initializer, which is not relevant in this case), and for both of them you can refer to Scott Meyers' Effective Modern C++.
auto v4 = T{};. How this expression (decltype(p){} or T{}) is treated by compiler? I know that {} part is for instantiation, but how the result type is not a reference type?
The result decltype(p){} is a reference type. It's the usage of auto that drops the const and reference qualifiers. The deduction of the type is explained here, and they're the same as those used for template type deduction. You could use decltype(auto) instead to keep those qualifiers (or, in this particular case, you could use const auto&).
Consider the following code:
class Test {
public:
static const int VALUE = 100;
};
std::tuple<int> foo(std::tuple<int> value) {
return value;
}
int main()
{
auto t = std::make_tuple(Test::VALUE); // This compiles fine
auto t1 = foo(std::make_tuple(Test::VALUE)); // undefined reference to `Test::VALUE' linker error
}
According to the other question (What does it mean to "ODR-use" something?) and the answer:
In plain word, odr-used means something(variable or function) is used in a context where the definition of it must be present.
In the first case (variable t) the variable VALUE is not odr-used because only it' value is needed. But in the second case why the code won't compile? If I would pass t instead of passed rvalue(?) the code would compile fine. How the second case differs from the first and why VALUE is odr-used here?
make_tuple takes the argument by const int& (since it's a constant lvalue and it takes a T&& argument), and binding a reference to a value is an ODR-use.
Both cases are ill-formed no diagnostic required. At high optimisation levels of gcc for example, the entire program is optimised out and there are no linker errors, where at no optimisation, both statements give linker errors.
To make it not an ODR use, you can convert it to an rvalue:
// Various ways to force the lvalue-to-rvalue conversion
auto t = std::make_tuple(int(Test::VALUE));
auto t1 = foo(std::make_tuple((void(), Test::VALUE)));
auto t2 = foo(std::make_tuple(+Test::VALUE));
(So std::make_tuple takes an int&& of a temporary)
Or you can make the definition inline (Easiest with constexpr):
class Test {
public:
static constexpr int VALUE = 100;
};
In the first case (variable t) the variable VALUE is not odr-used because only it' value is needed.
No. It's value is needed and it is ODR used.
But in the second case why the code won't compile?
Because the compiler you are using is not smart enough to optimize the second snippet of code. For example, both lines compile fine on gcc9.3 with -O1.
struct A{
constexpr operator bool()const{ return true; }
};
int main(){
auto f = [](auto v){ if constexpr(v){} };
A a;
f(a);
}
clang 6 accepts the Code, GCC 8 rejects it with:
$ g++ -std=c++17 main.cpp
main.cpp: In lambda function:
main.cpp:6:37: error: 'v' is not a constant expression
auto f = [](auto v){ if constexpr(v){} };
^
Who is correct and why?
When I take the parameter per reference, both reject the code:
struct A{
constexpr operator bool()const{ return true; }
};
int main(){
auto f = [](auto& v){ if constexpr(v){} };
constexpr A a;
f(a);
}
Compiled with clang 6:
$ clang++ -std=c++17 main.cpp
main.cpp:6:40: error: constexpr if condition is not a constant expression
auto f = [](auto& v){ if constexpr(v){} };
^
main.cpp:8:6: note: in instantiation of function template specialization
'main()::(anonymous class)::operator()<const A>' requested here
f(a);
^
1 error generated.
When I copy the parameter into a local variable both accept the code:
struct A{
constexpr operator bool()const{ return true; }
};
int main(){
auto f = [](auto v){ auto x = v; if constexpr(x){} };
A a;
f(a);
}
Edit: I am sure that the second and third cases will be handled correctly by both compilers. I don't know what the rule is, though.
In the first case I suspect that clang is right, because the case resembles the second. I would like to know if in the first case clang or GCC is correct and which rules in the second case makes the use of the not-constexpr variable v invalid and in the third case x valid.
Edit 2: First Question is clear now:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84421
clang was right, GCC 7 accepted the code as well. The bug will be fixed in the final version of GCC 8.
Clang is correct in all cases. [Full disclosure: I'm a Clang developer]
The question in all cases reduces to this: can we call a constexpr member function on v within a constant expression?
To answer this question, we need to look at [expr.const]p2, which says:
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.8.1), would evaluate one of the following expressions:
...
an id-expression that refers to a variable or data member of reference type unless the reference has a
preceding initialization and either
it is initialized with a constant expression or
its lifetime began within the evaluation of e;
...
None of the other rules prohibit any of your examples. In particular, you are allowed to name local variables in a constant expression if they are not of reference type. (You are not allowed to perform lvalue-to-rvalue conversions on them -- that is, read their values -- unless their value is known (for instance, because they're constexpr), and you're not allowed to end up referring to the address of such a variable, but you are allowed to name them.)
The reason that the rules are different for entities of reference type is that merely naming an entity of reference type causes the reference to be immediately resolved, even if you don't do anything with the result, and resolving a reference requires knowing what it's bound to.
So: the first example is valid. The *this value of the constexpr member function is bound to the local variable a. It doesn't matter that we don't know what object that is, because the evaluation doesn't care.
The second example (where v is of reference type) is ill-formed. Merely naming v requires resolving it to the object it's bound to, which can't be done as part of the constant expression evaluation because we have no idea what it'll end up being bound to. It doesn't matter that the later evaluation steps won't use the resulting object; references are resolved immediately when they're named.
The third example is valid for the same reason as the first. Notably, the third example remains valid even if you change v to be of reference type:
auto f = [](auto &v) { auto x = v; if constexpr (x) {} };
A a;
f(a);
... because x is, once again, a local variable that we can name within a constant expression.
const int ci = 10;
auto i = ci; // i will be "int" instead of "const int"
i = 20;
I am wondering why auto is designed for this kind of behaviour?
why the type i is "int" instead of "const int" ?
what is the concern here?
I think understand why will help us to remember it
auto mostly follows the same type deduction rules as template argument deduction. The only difference is that auto will deduce std::initializer_list from a braced-init-list in some cases, while template argument deduction doesn't do this.
From N3337, §7.1.6.4 [dcl.spec.auto]
6 ... The type deduced for the variable d is then
the deduced A determined using the rules of template argument deduction from a function call (14.8.2.1), ...
The behavior you're observing is the same as what template argument deduction would do when deducing types from a function call
§14.8.2.1 [temp.deduct.call]
2 If P is not a reference type:
— ...
— If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
Thus, in
auto i = ci;
the top level const qualifier is ignored and i is deduced as int.
When you write
auto& i = ci;
then i is no longer not a reference type and the above rule doesn't apply, so the const qualifier is retained.
auto by itself means that you want a new, locally-owned variable with a copy of the given value. const-ness is not part of value. An int is an int whether it's specified using a literal, a named constant, an expression, or a non-const variable.
auto i = 3,
j = i,
k = ci,
m = 3 + 4; // All these variables are type int.
To get a constant of deduced type, you can still use auto const. This expresses within the declaration how the variable may be used.
const auto i = 3;
Since C++14, there is also the decltype(auto) specifier which applies decltype to the initializer, to make a carbon copy of the given variable. Perhaps that's really what you expected:
decltype(auto) i = ci; // i receives type const int.
Live demo.
decltype(auto) is a bit tricky, though, and it has few use cases aside from its original purpose relating to deciding the return type of function call wrappers. Unless there's a good reason, choose const auto or const int instead.
Another alternative is to use a forwarding reference, spelled auto &&. This refers to the variable or value that initializes it, whatever that may be.
auto && i = ci; // i receives type const int & and aliases ci.
This is less expressive and specific, but reliably declares i as an alias to ci. The other thing you tried was auto &, which is similar but only allows forming a reference to a preexisting variable.
auto & i = ci; // i receives type const int & and aliases ci.
A reference to a const int variable must be of type const int &, because otherwise it would permit illegal modification.
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);
}