C++ lambda default arguments compiler misbehavior? - c++

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.

Related

In which context default argument of immediate function is substituted in C++20 (on source location example)?

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]

Why MacOS clang can't use C++17 std?

I want to use c++1z in MacOS10.14.4, like this g++ -std=c++1z test.cpp -o test. But the clang can't compile the code.
The error as follows.
In file included from test.cpp:3:
/Library/Developer/CommandLineTools/usr/include/c++/v1/any:599:5: error: static_assert failed due to requirement 'is_constructible<basic_string<char> &&, _RawValueType
&>::value' "ValueType is required to be an lvalue reference or a CopyConstructible type"
static_assert(is_constructible<_ValueType, _RawValueType &>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:29:19: note: in instantiation of function template specialization 'std::__1::any_cast<std::__1::basic_string<char> &&>' requested here
auto b = std::any_cast<std::string&&>(a); //< rvalue reference (no need for std::move)
^
1 error generated.
But the same code can work on Linux. The code as follows.
#include <string>
#include <iostream>
#include <any>
int main()
{
// simple example
auto a = std::any(12);
std::cout << std::any_cast<int>(a) << '\n';
try {
std::cout << std::any_cast<std::string>(a) << '\n';
}
catch(const std::bad_any_cast& e) {
std::cout << e.what() << '\n';
}
// advanced example
a = std::string("hello");
auto& ra = std::any_cast<std::string&>(a); //< reference
ra[1] = 'o';
std::cout << "a: " << std::any_cast<const std::string&>(a) << '\n'; //< const reference
auto b = std::any_cast<std::string&&>(a); //< rvalue reference (no need for std::move)
// Note, 'b' is a move-constructed std::string, 'a' is now empty
std::cout << "a: " << *std::any_cast<std::string>(&a) //< pointer
<< "b: " << b << '\n';
}
clang version: Apple LLVM version 10.0.1 (clang-1001.0.46.4)
And I use the gcc to compile this code, but didn't work either.
This seems to be from https://developercommunity.visualstudio.com/content/problem/200281/stdany-cast-static-assert-error-because-of-is-cons.html, which has an answer from Microsoft. The answer there was that this was a defect LWG 2768, so the fix may not have been implemented in older implementations of the standard library. For example, this compiles in clang 6.0.0 and gcc 7.4, but not clang 7.0.0 and gcc 8.1.
The reason is that you can't take an rvalue reference of an lvalue any. Fix this by either taking an lvalue reference and moving that or taking an rvalue reference of a moved any:
std::move(std::any_cast<std::string&>(a));
std::any_cast<std::string&&>(std::move(a));

Why constexpr implicit conversion doesn't always work?

#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.

C++ Macro causing :"warning: unused variable "LOG__METHOD__" "

I have a macro in my C++ code, macro has an unused variable. I am getting warning for that variable
the macro is to print the class and method name
#define LOG_ENTER(func_name, message) \
LOG_SET_METHOD(#func_name) \
LOG_MOD_INTERNAL(TC_TAG(ENTER) << message)
#define LOG_SET_METHOD(name) static const char LOG__METHOD__[] = "::" name "() ";
We are using gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC).
"warning: unused variable "LOG__METHOD__" "
How to suppress this warning? It causing more noise!!
The usual way of silencing this warning is to use the variable in a dummy expression :
int main() {
int i;
i;
}
However, this triggers "Warning : statement has no effect", because i has no side effect and its value is not used. To silence this one, we explicitly ignore the value :
int main() {
int i;
(void)i;
}
And there goes the warning.
A way to disable warning:
template <typename T>
void UnusedVar(const T&) {}
And then use:
UnusedVar(my_var);
Casting to void is also a common way (but doesn't work for all compiler):
(void) my_var; // or static_cast<void>(my_var)

Why is a lambda with = and this disallowed (yet GCC accepts)

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 [=].