I am curious about using auto keyword in C++11.
for function definition, you must write the return type of function:
auto f (int x) -> int { return x + 3; }; //success
auto f (int x) { return x + 3; }; //fail
but in this example the both of them will work:
auto f = [](int x) { return x + 3; }; //expect a failure but it works
auto f = [](int x) -> int { return x + 3; }; // this is expected code
thanks.
In C++11, a lambda expression can omit its return type if it can deduce the exact one without ambiguity. However, this rule doesn't apply to the regular function.
int f() { return 0; } // a legal C++ function
auto f() -> int { return 0; } // a legal C++ function only in C++11
auto f() { return 0; } // an illegal C++ function even if in C++11
If you are required to specify the return type after "->" (as it is in C+11) - what is the point to have "auto" in a function declaration? This is as if to say: "This function can return anything, but actually it is only this type". Uh?
Then if you are not required to specify the return type after "->" (as it is in C++14): imagine you don't have the source code of the function, but object file and the header. How do you know what the function returns (the return type)?
So far it seems that "auto" in a function declaration is yet another way to write non-readable code. As if in C++ there are not enough ways to do it already.
While inside function body "auto" is a nice "syntax sugar".
Or am I missing something?
Related
struct A
{
auto g1()
{
return true;
}
void f()
{
if (auto b = g1(); b) // ok
{
return;
}
if (auto b = g2(); b) // error: use of 'auto A::g2()' before deduction of 'auto'
{
return;
}
}
auto g2()
{
return true;
}
};
Why does C++17 if statement with initializer not work as expected?
Because the standard says so (quote from latest draft):
[dcl.spec.auto.general]
If a variable or function with an undeduced placeholder type is named
by an expression ([basic.def.odr]), the program is ill-formed. Once a
non-discarded return statement has been seen in a function, however,
the return type deduced from that statement can be used in the rest of
the function, including in other return statements.
[Example 4:
auto n = n; // error: n's initializer refers to n
auto f();
void g() { &f; } // error: f's return type is unknown
To add a little bit of clarification, the "declaration" of g2 is "seen" because the definition of g1 is in the complete-class context. But that does not extend to having seen the definition of g2.
The follwing code doesn't compile with g++/clang++.
constexpr int bar(int v) {
if (v > 0){
return v * 2;
}
return 2;
}
constexpr int foo(const int v) {
constexpr auto x = bar(v); // error
return v;
}
int main() {
constexpr auto a = foo(1);
constexpr auto b = bar(1); // ok
}
The error message is: x must be initailized by a constant expression
But from the line (ok) you see that bar() is constexpr.
if I change the body of foo() to
constexpr int foo(const int v) {
return bar(v);
}
its ok!
I don't get this clear, why the first form isn't possilble.
I used g++-6.2.1, g++-7.0.0 and clang++-3.9.0
The fix is this
constexpr int foo(const int v) {
auto x = bar(v);
return v;
}
The keyword constexpr means two very slightly different things. A constexpr variable must be evaluated at compile time, whereas a constexpr function must be possible to evaluate at compile time. There is nothing to prevent you from calling foo at runtime. This means...
The argument v is not necessarily constexpr.
When bar is called with b the answer might not be constexpr.
The result of cannot be stored in a constexpr variable as it might not be constexpr.
If foo is called at compile time then x is not stored, it is a temporary variable within the compiler, so making it constexpr doesn't make any sense.
The constexpr'ness of x can only make sense if foo is evaluated at runtime, in which case it cannot be constexpr, which cases an error.
I found a function defined this way on:
http://en.cppreference.com/w/cpp/language/decltype
I have never seen this syntax used to defined a function, can anyone explain this?
This only seems to work with auto and decltype
#include <iostream>
using namespace std;
auto f = [](int a, int b)->int
{
return a * b;
};
/*
int f = [](int a, int b) //DOES NOT WORK
{
return a * b;
};
*/
int main()
{
int a = 2, b = 3;
cout<<f(a,b);
return 0;
}
I'm not certain if the following function uses dectype when we do:
->int
If it does, then how?
auto f = [](int a, int b)->int
{
return a * b;
};
The f here is a global variable of some anonymous type, holding an instance of the anonymous function object defined after the operator=. Such anonymous function objects are called lambdas, they can occur everywhere where you can have some expression:
int main(int, char**) {
([](int a) {
cout << "lambda called with " << a << endl;
})(42);
return ([](int a, int b) { return a - 2 * b; })(42, 21);
}
The general syntax of such a lambda expression is the following:
"["CAPTURE"]" PARAMETERS ["mutable"] [RETURN] { BODY }
CAPTURE being a list of zero ore more of
variable from the enclosing scope, capture by value
variable from the enclosing scope, preceeded by &, capture by reference
& means capture all variables from the enclosing scope by reference
= means capture all variables from the enclosing scope by value
PARAMETERS are the usual parameter lists you know from functions, optionally (since C++14) with auto and type deduction.
mutable allows the lambda to mutate its captured variables.
The optional RETURN contains a specification of the return type, e.g. -> void and the BODY contains arbitrary expressions and statements.
Note that this is only a rough sketch of the syntax, but it should get you started. You can find out more about lambdas in the standard, searching for "C++11 lambda" on Google or for example here.
Btw, a lambda is nothing spooky, you can think of your f as being a rough equivalent of the following, "old style" C++ code:
struct {
int operator()(int a, int b) const {
return a * b;
}
} f;
When you use:
auto f = [](int a, int b)->int
{
return a * b;
};
the type of f is type of the lambda expression. It is not int, the return type of the lambda expression.
As far as the -> int part goes, that's the only syntax available to explicitly specify the return type of a lambda expression. It that is omitted, the return type is deduced by the compiler using an algorithm specified by the standard:
5.1.2 Lambda expressions
4 If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). If a lambda-expression does not include a trailing-return-type, it is as if the trailing-return-type denotes the
following type:
— if the compound-statement is of the form
{ attribute-specifier-seqopt return expression ; }
the type of the returned expression after lvalue-to-rvalue conversion (4.1), array-to-pointer conversion(4.2), and function-to-pointer conversion (4.3);
— otherwise, void.
[ Example:
auto x1 = [](int i){ return i; }; // OK: return type is int
auto x2 = []{ return { 1, 2 }; }; // error: the return type is void (a
// braced-init-list is not an expression)
—end example ]
This is really a C++14 question. And it's more theoretical than practical.
Sometimes you build a function's result in piecemeal:
int f( int x, int y )
{
int a;
//...
return a;
}
but if you change the return type, you have to change the type of "a" too. I get around this with a special declaration:
int f( int x, int y )
{
decltype( f(x,y) ) a;
//...
return a;
}
(For the newbies: what is the pitfall if a function parameter uses an r-value reference? Hint: we need std::forward to fix it.) A question randomly popped into my head: what if I use the new C++14 feature of "decltype( auto )" as the return type?! Will we get a recursive black hole? Or an error that it's not allowed? Would adding an initializer to "a" make everything all-right?
A question randomly popped into my head: what if I use the new C++14
feature of "decltype( auto )" as the return type?!
The example the OP is referring to is:
auto f( int x, int y ) // using C++1y's return type deduction
{
decltype( f(x,y) ) a;
//...
return a;
}
Will we get a recursive black hole? Or an error that it's not allowed?
It's not allowed [dcl.spec.auto]/11:
If the type of an entity with an undeduced placeholder type is needed
to determine the type of an expression, the program is ill-formed.
Once a return statement has been seen in a function, however, the
return type deduced from that statement can be used in the rest of the
function, including in other return statements.
Would adding an initializer to a make everything all-right?
Ex.
decltype( f(x,y) ) a = 42;
No; the use of decltype requires determining the return type of f. However, the following is possible:
auto a = 42;
From a comment:
so I could have a quick-and-dirty if & return block at the beginning of the function, then use the decltype(f(X)) construct afterwards (for the rest of the function)?
Yes, e.g.
auto f( int x, int y ) // using C++1y's return type deduction
{
if(false) return int();
decltype( f(x,y) ) a;
//...
return a;
}
However, I'd prefer either:
auto f( int x, int y ) // using C++1y's return type deduction
{
int a; // specifying the return type of `f` here
//...
return a;
}
or
auto f( int x, int y ) // using C++1y's return type deduction
{
auto a = 42; // specifying the return type of `f` via the initializer
//...
return a;
}
Consider this C++1y code (LIVE EXAMPLE):
#include <iostream>
auto foo();
int main() {
std::cout << foo(); // ERROR!
}
auto foo() {
return 1234;
}
The compiler (GCC 4.8.1) generously shoots out this error:
main.cpp: In function ‘int main()’:
main.cpp:8:18: error: use of ‘auto foo()’ before deduction of ‘auto’
std::cout << foo();
^
How do I forward-declare foo() here? Or maybe more appropriately, is it possible to forward-declare foo()?
I've also tried compiling code where I tried to declare foo() in the .h file, defined foo() just like the one above in a .cpp file, included the .h in my main.cpp file containing int main() and the call to foo(), and built them.
The same error occurred.
According to the paper it was proposed in, N3638, it is explicitly valid to do so.
Relevant snippet:
auto x = 5; // OK: x has type int
const auto *v = &x, u = 6; // OK: v has type const int*, u has type const int
static auto y = 0.0; // OK: y has type double
auto int r; // error: auto is not a storage-class-specifier
auto f() -> int; // OK: f returns int
auto g() { return 0.0; } // OK: g returns double
auto h(); // OK, h's return type will be deduced when it is defined
However it goes on to say:
If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed. But once a return statement has been seen in a function, the return type deduced from that statement can be used in the rest of the function, including in other return statements.
auto n = n; // error, n's type is unknown
auto f();
void g() { &f; } // error, f's return type is unknown
auto sum(int i) {
if (i == 1)
return i; // sum's return type is int
else
return sum(i-1)+i; // OK, sum's return type has been deduced
}
So the fact that you used it before it was defined causes it to error.