I created a simple lambda as below and it works as expected (GCC 4.6.4 and 4.7.2 -- demo). But then I checked the standard and 5.1.2-8 explicitly forbids using an = and this in the lambda captures.
... If a lambda-capture includes a capture-default that is =, the lambda-capture shall not contain this and each identifier it contains shall be preceded by &. ...
Am I reading something wrong and this is actually allowed (though the example definitely shows this as forbidden)? If no, then I'm having trouble understanding why it isn't allowed. And also, does that mean GCC is wrong to allow it?
#include <iostream>
#include <functional>
using namespace std;
struct sample {
int a;
std::function<int()> get_simple(int o) {
return [=,this]() {
return a + o;
};
}
};
int main() {
sample s;
auto f = s.get_simple(5);
s.a = 10;
cout << f() << endl; //prints 15 as expected
}
You don't need to capture the "this" field if you are already specifying a default capture mode as you are in this case by setting [=]. See below where I am explicitly passing the "this" and the o by value. Therefore, the warning is telling you that you are redundantly passing the "this" in this case because you automatically get a "this" when specifying either = or & as a default capture mode. Therefore only specify "this" when you are not specifying a default capture mode. See below.
#include <iostream>
#include <functional>
using namespace std;
struct sample {
int a;
std::function<int()> get_simple(int o)
{
return [o,this]{ return a + o; };
}
};
int main() {
sample s;
auto f = s.get_simple(5);
s.a = 10;
cout << f() << endl; //prints 15 as expected
}
With your code compiled in GCC 4.8.1, and with the -Wall flag, compilation gives out the following warning:
main.cpp: In member function ‘std::function
sample::get_simple(int)’:
main.cpp:10:19: warning: explicit by-copy capture of ‘this’ redundant
with by-copy capture default [enabled by default]
return [=,this]() {
^
I think this is made by GCC to be compatible with pre-C++11 standard lambdas.
This is now allowed by P0409R2.
Further, implicit this in [=] is deprecated by P0806R2. Try Clang++ 8 -std=c++2a, for example, you will get the warning [-Wdeprecated-this-capture] for [=].
Related
In C++20 a new feature was added to get the source location information: https://en.cppreference.com/w/cpp/utility/source_location
Here is a slightly modified example from that page, where an addition immediate function loc is used to get the source location:
#include <iostream>
#include <string_view>
#include <source_location>
consteval auto loc(std::source_location x = std::source_location::current() ) { return x; }
void log(const std::string_view message,
const std::source_location location = loc()) {
std::cout << "file: "
<< location.file_name() << "("
<< location.line() << ":"
<< location.column() << ") `"
<< location.function_name() << "`: "
<< message << '\n';
}
template <typename T> void fun(T x) { log(x); }
int main(int, char*[]) {
log("Hello world!");
fun("Hello C++20!");
}
In the latest MSVC 2019 it prints as in the original example from cppreference.com:
file: main.cpp(25:5) `main`: Hello world!
file: main.cpp(20:5) `fun`: Hello C++20!
But in GCC the same line is indicated twice in the output:
file: /app/example.cpp(8:51) ``: Hello world!
file: /app/example.cpp(8:51) ``: Hello C++20!
demo: https://gcc.godbolt.org/z/nqE4cr9d4
Which of the compilers is right here?
And if define loc function not as immediate one:
auto loc(std::source_location x = std::source_location::current() ) { return x; }
then the output of GCC changes and resembles the original example:
file: /app/example.cpp(20:8) `int main(int, char**)`: Hello world!
file: /app/example.cpp(17:42) `void fun(T) [with T = const char*]`: Hello C++20!
While MSVC refuses to compile it with the error:
error C7595: 'std::source_location::current': call to immediate function is not a constant expression
demo: https://gcc.godbolt.org/z/vorW4f9ax
Please also suggest, which compiler is right in not-immediate case as well?
This is explained in 17.8.2.1.2 [support.srcloc.class] (emphasis mine):
Remarks: Any call to current that appears as a default member initializer (11.4), or as a subexpression
thereof, should correspond to the location of the constructor definition or aggregate initialization that
uses the default member initializer. Any call to current that appears as a default argument (9.3.3.6),
or as a subexpression thereof, should correspond to the location of the invocation of the function that
uses the default argument (7.6.1.2).
From this, I deduce that GCC is right.
When the call to current happens in line 5, it returns a source_location object that "correspond[s] to the location of the invocation of the function (in this case the function loc) that
uses the default argument".
In this case the invocation location is 8:51 (the expression const std::source_location location = loc()).
Why the function name is empty is explained by the following:
17.8.2.1.1.1 (Table 38) tells us that the function name should be "such as in" __func__.
Element
Value
function_name_
A name of the current function such as in __func__ (9.5.1) if any, an empty string otherwise.
9.5.1.8 Example shows that if __func__ appears as a default argument, the name is undefined. I know that examples are nonnormative text, but this clearly describes the intent:
[Example:
struct S {
S() : s(__func__) { } // OK
const char* s;
};
void f(const char* s = __func__); // error: __func__ is undeclared
— end example]
I ran the following C program and got In as the output.
#include<stdio.h>
int main()
{
auto int i = 0;
if(++i)
printf("In");
else
printf("Out");
return 0;
}
But when I tried to run it as a C++ program by changing the header files and standard output, I got an error:
jdoodle.cpp: In function ‘int main()’:
jdoodle.cpp:6:14: error: two or more data types in declaration of ‘i'.
6 | auto int i = 0;
| ^.
jdoodle.cpp:7:10: error: ‘i’ was not declared in this scope.
7 | if(++i).
| ^
C++ code:
#include <iostream>
using namespace std;
int main() {
auto int i = 0;
if(++i)
cout<<"In";
else
cout<<"Out";
return 0;
}
In C, auto is an obsolete keyword it inherited from B. It is either implicitly assumed, or illegal to specify. So, it's basically never used.
C++ inherited this keyword from C as is, and your code would have compiled under C++98. In C++11 this keyword was repurposed to be used for implicit type deduction. It is now widely used but means something completely different and the way you tried to use it is illegal.
The point is, C and C++ are different languages. Writing code that compiles under C and C++ is difficult and requires care.
In the C++ program, for the line
auto int i = 0;
the auto keyword is being used to automatically deduce the type of variable i from its initialized value if compiling with C++11 or later. However, the line also includes int, which is also declaring the type. You can't use both of them -- you get the same error if you write double int i = 0; (i can't be both a double and an int) or int int i = 0; (it's the same type but you're declaring the type twice). Choose one or the other, i.e. either
auto i = 0;
or
int i = 0;
You can see that it works with C++98 but not C++11 or C++14 online here. Prior to C++11 the auto keyword was a storage class specifier.
Which of the following C++ lambdas/statements are supposed to work according to the latest C++ specification?
Context in case this is relevant: see here.
I tested the following code snippets with -std=c++17 on Fedora 33 with clang 11.0.0 and gcc 10.2.1.
Update: Replace __PRETTY_FUNCTION__ with __func__ for standard compliance. The same behavior can be observed.
Update2: Example using const char * s = __func__ as default argument to verify that it should be valid within a function scope (thanks to #BenVoigt).
1. LLVM __func__ within lambda default argument
void clang() {
[](const char* c = __func__) {std::cout << c << std::endl;}();
}
Expected behavior (CLANG):
Print out clang\n (void clang() for __PRETTY_FUNCTION__)
Observed behavior (CLANG):
Compiler warning: warning: predefined identifier is only valid inside function [-Wpredefined-identifier-outside-function]
Print out \n (top level() for __PRETTY_FUNCTION__)
2. GCC ignores statements
template <typename L>
constexpr std::string_view methodName(L l) { return l(); }
#define __METHOD_NAME__ (\
__func__, /* needed for pointer to work */ \
methodName([](const char* c = __func__) {return std::string_view(c);}) \
)
void gcc1() {
std::cout << [](const char* c = __func__) { return c; }() << std::endl; // GCC: This statement doesn't do anything
std::cout << [](const char* c = __func__) { return c; }("gcc") << std::endl;
std::cout << __METHOD_NAME__ << std::endl; // GCC: This statement somehow conflicts with the statements above
}
void gcc2() {
std::cout << __METHOD_NAME__ << std::endl; // GCC: This statement itself works
}
Expected output (GCC):
gcc1
gcc
gcc1
gcc2
Observed output (GCC):
gcc
gcc2
3. GCC Compile error
void gcc3() {
std::string_view s = [](const char* c = __func__) { return std::string_view(c); }();
std::cout << s << std::endl;
}
Expected behavior (GCC): Compiles without problems.
Observed behavior (GCC): error: internal compiler error: in finish_expr_stmt
[class.local] The local class is in the scope of the enclosing scope, and has the same access to names outside the function as does the enclosing function. [Note: A declaration in a local class cannot odr-use (6.2) a local entity from an enclosing scope. — end note]
A lambda is a local class, and as such it cannot use variables from the enclosing scope (e.g. __func__) other than in its capture clause.
#include <iostream>
struct Index {
constexpr operator int() const { return 666; }
};
template <int i> void foo() {
std::cout << i << std::endl;
}
void wrapper(Index index) {
foo<index>();
}
int main() {
Index index;
// foo<index>(); // error: the value of ‘index’ is not usable in a constant expression
wrapper(index);
}
Hello, everyone.
I'm using a constexpr conversion of a variable "index" to an int value, which is substituted to a "foo" templated function.
If I directly call foo<index>() from "main", I get a compiler error.
If the same call is done from the "wrapper", then everything compiles and works fine.
What am I missing there?
Compile command: g++ -std=c++14 main.tex with g++ (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6).
A very similar error was reported here. It was first reported with GCC 4.9.0
In the analysis provided:
This is a GCC bug. It appears to be some sort of confusion in the way GCC handles internal linkage non-type template parameters of pointer type.
It has since been resolved.
This question already has an answer here:
Operator cast, GCC and clang: which compiler is right?
(1 answer)
Closed 6 years ago.
Consider following program:
struct S {
using T = float;
operator T() { return 9.9f; }
};
int main() {
S m;
S::T t = m;
t = m.operator T(); // Is this correct ?
}
The program compiles fine in g++ ( See live demo here )
But it fails in compilation in clang++, MSVC++ & Intel C++ compiler
clang++ gives following errors ( See live demo here )
main.cpp:8:20: error: unknown type name 'T'; did you mean 'S::T'?
t = m.operator T(); // Is this correct ?
^
S::T
main.cpp:2:11: note: 'S::T' declared here
using T = float;
MSVC++ gives following errors ( See live demo here )
source_file.cpp(8): error C2833: 'operator T' is not a recognized operator or type
source_file.cpp(8): error C2059: syntax error: 'newline'
Intel C++ Compiler also rejects this code ( See live demo here )
So, the question is which compiler is right here ? Is g++ incorrect here or other 3 compilers are incorrect here ? What C++ standard says about this ?
[basic.lookup.classref]/7:
If the id-expression is a conversion-function-id, its conversion-type-id is first looked up in the class of the object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire
postfix-expression. In each of these lookups, only names that denote types or templates whose specializations are types are considered. [ Example:
struct A { };
namespace N {
struct A {
void g() { }
template <class T> operator T();
};
}
int main() {
N::A a;
a.operator A(); // calls N::A::operator N::A
}
— end example]
This indicates that the example could be fine, although in the above example, A has previously been declared as a type name, visible to main.
This was discussed in core issue 156, filed all the way back in 1999:
How about:
struct A { typedef int T; operator T(); };
struct B : A { operator T(); } b;
void foo() {
b.A::operator T(); // 2) error T is not found in the context
// of the postfix-expression?
}
Is this interpretation correct? Or was the intent for this to be an
error only if T was found in both scopes and referred to different
entities?
Erwin Unruh: The intent was that you look in both contexts. If you find it only once, that's the symbol. If you find it in both, both symbols must be "the same" in some respect. (If you don't find it, its an error).
So I'd say that Clang is wrong: the intent, as expressed in the wording to some extent, is that we find T, even if only in the class.