I had a Q&A before: Point of declaration in C++. The rule point-of-declaration nicely is applicable on many situations. Now, I confused on usage of auto in combination of this rule.
Consider these two codes:
i. Declaring x by itself (we don't expect it to work):
{
auto x = x;
}
ii. Declaring the inner x by the outer x (It makes error in gcc 4.8.x):
{
int x = 101; // the outer x
{
auto x = x; // the inner x
}
}
According to the rule of point-of-declaration, it should work but it doesn't. It seems there is another rule in the standard that I missed it. The question is, Where is the point-of-declaration when using auto?
There are two possibilities:
i. If the point of declaration is after =, at the end of statement:
auto object = expression;
^
Is it here? If it is, why gcc complains?
So the second declaration is valid and must work, because there is no x but that outer one (which is declared before). Therefore auto x=x is valid and the inner x should be assigned to 101.
ii. If the point of declaration is before = :
auto object = expression;
^
Well, it doesn't make any sense because auto has to wait until see the following expression. For example auto x; is invalid.
Update: I need an answer which explains it by the rule point of declaration.
auto x = x; // inner x
is ill-formed.
To quote from the C++11 standard (emphasis mine):
7.1.6.4 auto specifier
...
3 Otherwise, the type of the variable is deduced from its initializer. The name of the variable being declared
shall not appear in the initializer expression. ...
And so because x after = resolves to the x in auto x (as explained in the question you linked), that above piece of code is ill-formed.
Just like in any other kind of definition, the x on the right-hand side of the initialiser for auto x = x resolves to the local auto x. C++ has always done this (i.e. int x = x compiles but will give you undefined behaviour).
The reason auto x = x fails to compile is because while x is in scope it has no known type yet, and so using it as the initialiser fails because the type can't be deduced from the expression.
Just like any other kind of declaration, x is in scope after its declarator which is auto x.
int x = 10;
int y = 20;
{
int x = x; // This is NOT the outer x. This is undefined behaviour (reading an
// uninitialised variable).
auto y = y; // This is NOT the outer y. This is a compile error because the type of
// y is not known.
}
Just adding an example with more explicit diagnostics:
auto ll = [&] { ll(); };
Results in (gcc):
error: variable ‘auto ll’ with ‘auto’ type used in its own initializer
or (clang):
error: variable 'll' declared with 'auto' type cannot appear in its own initializer
auto ll = [&] { ll(); };
^
You can see that there is an explicit rule for this. I haven't looked at the specs.
The compiler reads a whole statement (from the beginning of a line until the next semi-colon) and then evaluates the different parts of a statement using priorities of operations, and then when the time comes when the value of auto x is to be assigned, the type that came up after th = sign gets taken.
For example:
template <typename T>
T sum(T a, T b)
{
return a+b;
}
int main()
{
auto x = sum<double>(1,5); // x here is a double, because the return value is double
auto y = sum<int>(1,7); //y is an int, because the return value is int
}
And about your auto x = x, you're redefining the same variable name. That's invalid! auto y = x shall work.
Related
I found the results are different across compilers if I use a lambda to capture a reference to global variable with mutable keyword and then modify the value in the lambda function.
#include <stdio.h>
#include <functional>
int n = 100;
std::function<int()> f()
{
int &m = n;
return [m] () mutable -> int {
m += 123;
return m;
};
}
int main()
{
int x = n;
int y = f()();
int z = n;
printf("%d %d %d\n", x, y, z);
return 0;
}
Result from VS 2015 and GCC (g++ (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609):
100 223 100
Result from clang++ (clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)):
100 223 223
Why does this happen? Is this allowed by the C++ Standards?
A lambda can't capture a reference itself by value (use std::reference_wrapper for that purpose).
In your lambda, [m] captures m by value (because there is no & in the capture), so m (being a reference to n) is first dereferenced and a copy of the thing it is referencing (n) is captured. This is no different than doing this:
int &m = n;
int x = m; // <-- copy made!
The lambda then modifies that copy, not the original. That is what you are seeing happen in the VS and GCC outputs, as expected.
The Clang output is wrong, and should be reported as a bug, if it hasn't already.
If you want your lambda to modify n, capture m by reference instead: [&m]. This is no different than assigning one reference to another, eg:
int &m = n;
int &x = m; // <-- no copy made!
Or, you can just get rid of m altogether and capture n by reference instead: [&n].
Although, since n is in global scope, it really doesn't need to be captured at all, the lambda can access it globally without capturing it:
return [] () -> int {
n += 123;
return n;
};
I think Clang may actually be correct.
According to [lambda.capture]/11, an id-expression used in the lambda refers to the lambda's by-copy-captured member only if it constitutes an odr-use. If it doesn't, then it refers to the original entity. This applies to all C++ versions since C++11.
According to C++17's [basic.dev.odr]/3 a reference variable is not odr-used if applying lvalue-to-rvalue conversion to it yields a constant expression.
In the C++20 draft however the requirement for the lvalue-to-rvalue conversion is dropped and the relevant passage changed multiple times to include or not include the conversion. See CWG issue 1472 and CWG issue 1741, as well as open CWG issue 2083.
Since m is initialized with a constant expression (referring to a static storage duration object), using it yields a constant expression per exception in [expr.const]/2.11.1.
This is not the case however if lvalue-to-rvalue conversions are applied, because the value of n is not usable in a constant expression.
Therefore, depending on whether or not lvalue-to-rvalue conversions are supposed to be applied in determining odr-use, when you use m in the lambda, it may or may not refer to the member of the lambda.
If the conversion should be applied, GCC and MSVC are correct, otherwise Clang is.
You can see that Clang changes it behavior if you change the initialization of m to not be a constant expression anymore:
#include <stdio.h>
#include <functional>
int n = 100;
void g() {}
std::function<int()> f()
{
int &m = (g(), n);
return [m] () mutable -> int {
m += 123;
return m;
};
}
int main()
{
int x = n;
int y = f()();
int z = n;
printf("%d %d %d\n", x, y, z);
return 0;
}
In this case all compilers agree that the output is
100 223 100
because m in the lambda will refer to the closure's member which is of type int copy-initialized from the reference variable m in f.
This is not allowed by the C++17 Standard, but by some other Standard drafts it might be. It's complicated, for reasons not explained in this answer.
[expr.prim.lambda.capture]/10:
For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the referenced type if the entity is a reference to an object, an lvalue reference to the referenced function type if the entity is a reference to a function, or the type of the corresponding captured entity otherwise.
The [m] means that the variable m in f is captured by copy. The entity m is a reference to object, so the closure type has a member whose type is the referenced type. That is, the member's type is int, and not int&.
Since the name m inside the lambda body names the closure object's member and not the variable in f (and this is the questionable part), the statement m += 123; modifies that member, which is a different int object from ::n.
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.
I had always avoided initializations like the following
const auto& x = a, y = b;
const int* const x = ptr_1, *const y = ptr_2; // wot
For the reason that the reference and pointer qualifiers don't apply to both the initializations. Granted it's one of the first things beginners learn, the ambiguity associated with it makes me feel like the following is clearer and requires less thought on the reader's end
const auto& x = a;
const auto& y = b;
With C++17 and structured bindings I was happy and saw lots of potential. C++17 outlawed what C++14 and C++11 had failed to fix, auto x {1} is an int and not std::initializer_list<int>. But why does the following code not work?
const auto& [x, y] {a, b};
const auto& [x, y] = {a, b};
The latter is in line with the new rules for auto deduction and initializer lists, the expression on the right hand side is treated as an initializer list. But for the former compilation fails with the following error
initializer for variable '[a, b]' with type 'const auto &' contains multiple expressions
Is there any way I can declare both x and y with the structured bindings syntax without having to resort to tuples, pairs and the like? Also why is the former in the code example above ill formed code? Is there an ambiguity in that syntax?
Structured binding is, so to speak, for "unpacking" things. It's not designed to be a way to combine normal declarations. That const auto& applies to neither a nor b, despite the appearance.
Your particular attempt violates [dcl.dcl]/8:
A simple-declaration with an identifier-list is called a
structured binding declaration ([dcl.struct.bind]). [...] The initializer shall be of the form “= assignment-expression ”, of the form “{ assignment-expression }”, or
of the form “( assignment-expression )”, where the
assignment-expression is of array or non-union class type.
int a = 1, b = 2;
const auto bitand <:x, y:> = std::tie(a, b);
This structured binding declaration is (very) roughly equivalent to
const auto bitand __e = std::tie(a, b); // hidden variable
auto and x = std::get<0>(__e);
auto and y = std::get<1>(__e);
(The real thing uses tuple_element, not auto.)
Notes:
The const auto bitand applies to the hidden variable and only the hidden variable. x and y are always references even if you write just auto; whether their referent is const depends on the const propagation properties of the initializer's type.
A temporary materialized from a prvalue initializer will have its lifetime extended by the reference binding.
In this example, both x and y are of type "reference to int"; it is valid to write x = 1;.
There's special treatment for structured bindings in the decltype wording.
These semantics are unsurprising if we are talking about unpacking a struct, etc., with two "reference to int" members; a const on such things doesn't actually affect the referent's constness. OTOH, you are in for a bad surprise if you want to use structured binding declarations for something they aren't designed to do.
This syntax is just not supported. You can only unpack aggregate classes and objects which for which std::get has been overloaded: https://skebanga.github.io/structured-bindings/
Unfortunately, you cannot really make use of the cool deduction guide because you want a reference to a and not to the tuple member. Thus you have to write out the template parameter list.
#include <tuple>
int main()
{
int a = 1;
int b = 2;
const auto& [x, y] = std::tuple<int&,int&>{a, b};
}
You could also not be as stupid as me and read the docs correctly.
#include <tuple>
int main()
{
int a = 1;
int b = 2;
const auto& [x, y] = std::forward_as_tuple(a, b);
}
const auto& [x, y] = std::tie(a, b); works as well.
class A {
public:
int a;
char b;
double c;
A ( int x, char y, double z ) : a(x), b(y), c(z){}
};
int main(){
auto lambda = []( auto x ) {
static auto y = x;
// y = x;
return y;
};
int a = lambda(1);
char b = lambda('a');
double c = lambda(1.5);
A d = lambda( A( 2, 'b', 2.5 ) );
return 0;
}
This code compiles in Clang 3.8.0 and GCC 5.4.0, and works fine. However, taking into account that variable y is static:
What is the type of variable y? Does the type of y change in every call to the lambda?
Is variable y initialized in every call in spite of being static? The commented assignment // y = x is not needed to update the value of variable y.
Is this behaviour C++14 Standard compliant?
If I print the sizeof(y) in each call I get 4, 1, 8 and 16 respectively.
On local and global static variables in C++
Your lambda is generic. Which means that this is a template in disguise. The situation is handled in accordance with general rules for template specializations.
For every specific deduced type of parameter x you get a separate specialization of your function. So, yes for each specific type of x you get a separate copy of y. But the underlying mechanism is not somehow localized at your static, it is the whole body of your function that gets "copied" to produce a separate independent implementation of the function.
Each specialization of your lambda will have a separate copy of y, which will be initialized only once at the first call to that specific specialization.
The situation is virtually equivalent to a more explicit
template <typename T> void foo(T x)
{
static T y = x;
std::cout << y << std::endl;
}
int main()
{
foo(1);
foo('a');
foo(1.5);
foo(3.0);
}
which outputs 1, a, 1.5 and 1.5.
In this example you get three independent specializations of foo: foo<int>, foo<char> and foo<double>. Each version of foo gets its own version of y, meaning that there are three different static ys in this example. The first call to each specialization will initialize y and the subsequent calls will not re-initialize it. In this case call to foo(1.5) initializes y for foo<double>, but the subsequent call to foo(3.0) does not.
The same thing happens in your case as well, it just uses a different syntax.
Lamda with auto is nothing more than a class with operator () overloaded to be a template and using auto for type deduction (which follows the rules for a template parameter deduction).
In this case you have as many static y fields as many instantiations of this function object template operator.
The initialization is done like in hand written class which is the first time the function is triggered (the lambda in this case)
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);
}