Trying to compile this code:
const int a = 1;
auto lambda = [&]() {
&a;
};
lambda();
On clang++ everything is fine, but g++ gives an error:
error: lvalue required as unary ‘&’ operand
I haven't found anything explaining such behavior. Is it a bug in g++? Or does clang++ miss something?
It's considered to be a bug in g++: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58894
According to comments, it lasts from GCC 4.5.4 and, at that moment, not fixed in GCC 4.9.0.
Related
The following code compiles fine on Visual Studio 2019 and on gcc 10.2 (and other gcc versions) with -std=c++11 but fails on clang (versions 9, 10 and 11).
#include <map>
#include <string>
struct repo {
static constexpr const char *x = "sth";
};
int main() {
// 1) This compiles
std::map<std::string, int> m1 = { {repo::x, 3} };
// 2) This compiles
std::map<std::string, std::string> m2 = { std::make_pair(repo::x, "") };
// 3) This does not compile on clang
std::map<std::string, std::string> m3 = { {repo::x, ""} };
return 0;
}
The error from clang is:
... undefined reference to `repo::x'
clang-11: error: linker command failed with exit code 1 (use -v to see invocation)
Compiler returned: 1
There are similar questions on SO on this, i.e. Undefined reference error, static constexpr data member, but none that explain to me why this code does not compile on clang. Is there an issue with the code in 3) above?
C++17 introduces the rule that static constexpr member variables are implicitly inline (P0386). Inline variables didn't exist before C++17.
This means that in earlier C++ standards, a compiler may need a static constexpr member variable to be defined. If its address is taken, for example.
In C++ standards earlier than C++17, you can ensure that your code is well-formed, by separately defining the static variable.
struct repo {
static constexpr const char *x = "sth";
};
constexpr const char *repo::x;
Edit:
It should be noted that with optimizations off, none of the examples successfully link.
It is an artifact of the optimizer that sometimes, a reference to a value can be flattened to the value itself, in which case the linker won't look for the missing symbol.
Using https://godbolt.org/z/edj6Ed I verified that the program compiles & runs for gcc >= 7, but fails with the same error as your clang for 6.4, provided that one uses
-std=c++17
(actually I used -pedantic -std=c++17).
Clang++ also compiles your program fine starting from version 5.0.0, the first to support C++17. It always fails with std=C++14.
What happened in C++17 that refers to static constexpr?
A constexpr specifier used in a function or static member variable (since C++17) declaration implies inline.
(see https://en.cppreference.com/w/cpp/language/constexpr )
See also: https://www.codingame.com/playgrounds/2205/7-features-of-c17-that-will-simplify-your-code/inline-variables
Pre-C++17, you need to have a definition of static constexpr variable if it is ODR-used.
In your example, x is ODR-used, since it is passed to as an argument to pair constructor, which takes values by reference. Thus, you need a definition for it.
Consider the following example (snippet (0)):
struct X
{
constexpr int get() const { return 0; }
};
void foo(const X& x)
{
constexpr int i = x.get();
}
int main()
{
foo(X{});
}
The above example compiles with all versions of g++ prior to g++ 10.x, and never compiled under clang++. The error message is:
error: 'x' is not a constant expression
8 | constexpr int i = x.get();
|
live example on godbolt.org
The error kind of makes sense, as x is never a constant expression in the body of foo, however:
X::get() is marked constexpr and it does not depend on the state of x;
Changing const X& to const X makes the code compile with every compiler (on godbolt.org) snippet (1).
It gets even more interesting when I mark X::get() as static ((on godbolt.org) snippet (2)). With that change, all tested versions of g++ (including trunk) compile, while clang++ still always fail to compile.
So, my questions:
Is g++ 9.x correct in accepting snippet (0)?
Are all compilers correct in accepting snippet (1)? If so, why is the reference significant?
Are g++ 9.x and g++ trunk correct in accepting snippet (2)?
Is g++ 9.x correct in accepting snippet (0)?
No.
Are all compilers correct in accepting snippet (1)? If so, why is the reference significant?
Yes, they are.
A constant expression cannot use an id-expression naming a reference that doesn't have a previous constant expression initialization or began its lifetime during the constant expression evaluation. [expr.const]/2.11 (same in C++20)
The same is not true if you are naming a non-reference variable without involving any lvalue-to-rvalue conversion. x.get() only refers to x as lvalue and only calls a constexpr function that doesn't actually access any member of x, so there is no issue.
Are g++ 9.x and g++ trunk correct in accepting snippet (2)?
No, because the expression still contains the subexpression x which violates the rule mentioned above.
The following code compiles successfully with g++ 7.3.0 and fails to compile with clang++ 6.0.0 (compilation flags are -std=c++17 -Wall -Wextra -Werror -pedantic-errors):
auto foo = [](auto, auto... tail) {
if constexpr (sizeof...(tail) > 0)
{
return foo(tail...);
}
else
{
return 42;
}
};
int main()
{
}
clang++ compilation error message:
error: variable 'foo' declared with deduced type 'auto' cannot appear in its own initializer
return foo(tail...);
What behavior is standard compliant in this case?
Clang is right to reject this according to [dcl.spec.auto]/10, as of C++17.
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.
The type of foo is needed to resolve the recursive call (find operator(), etc). It's needed to determine the closure type. Since the closure type is being deduced here... you see where it goes.
GCC may prove it's not always impossible to get around it, but in general the standard prohibits it.
Which is the correct behaviour for the following program?
// example.cpp
#include <iostream>
#include <memory>
struct Foo {
void Bar() const {
std::cout << "Foo::Bar()" << std::endl;
}
};
std::shared_ptr<Foo> MakeFoo() {
return std::make_shared<Foo>();
}
int main() {
auto p { MakeFoo() };
p->Bar();
}
When I compile it in my Linux RHEL 6.6 workstation, I obtain the following results:
$ g++ -v
gcc version 5.1.0 (GCC)
$ g++ example.cpp -std=c++14 -Wall -Wextra -pedantic
$ ./a.out
Foo::Bar()
but
$ clang++ -v
clang version 3.6.0 (trunk 217965)
$ clang++ example.cpp -std=c++14 -Wall -Wextra -pedantic
example.cpp:16:4: error: member reference type 'std::initializer_list<std::shared_ptr<Foo> >' is not a pointer; maybe you meant to use '.'?
p->Bar();
~^~
example.cpp:16:6: error: no member named 'Bar' in 'std::initializer_list<std::shared_ptr<Foo> >'
p->Bar();
~ ^
2 errors generated.
and
$ icpc -v
icpc version 15.0.3 (gcc version 5.1.0 compatibility)
$ icpc example.cpp -std=c++14 -Wall -Wextra -pedantic
example.cpp(16): error: expression must have pointer type
p->Bar();
^
compilation aborted for example.cpp (code 2)
Tl;DR
This behavior is subject to a proposal and an Evolution Working Group issue. There is some ambiguity as to whether this is considered a C++14 defect or a C++1z proposal. If it turns out to be a C++14 defect then gcc's behavior is correct for C++14. On the other hand if this is really a C++1z proposal then clang and icpc are exhibiting correct behavior.
Details
It looks like this case is covered by N3681 which says:
Auto and braced initializers cause a teachability problem; we want to
teach people to use uniform initialization, but we need to
specifically tell programmers to avoid braces with auto. In C++14, we
now have more cases where auto and braces are problematic; return type
deduction for functions partially avoids the problem, since returning
a braced-list won't work as it's not an expression. However, returning
an auto variable initialized from a braced initializer still returns
an initializer_list, inviting undefined behaviour. Lambda init
captures have the same problem. This paper proposes to change a
brace-initialized auto to not deduce to an initializer list, and to
ban brace-initialized auto for cases where the braced-initializer has
more than one element.
and provides the following examples:
auto x = foo(); // copy-initialization
auto x{foo}; // direct-initialization, initializes an initializer_list
int x = foo(); // copy-initialization
int x{foo}; // direct-initialization
So I think clang is currently correct, the latest version of clang provides this warning:
warning: direct list initialization of a variable with a deduced type
will change meaning in a future version of Clang; insert an '=' to
avoid a change in behavior [-Wfuture-compat]
From EWG issue 161 that N3922 was adopted for this.
As Praetorian notes the proposal recommends this is a C++14 defect:
Direction from EWG is that we consider this a defect in C++14.
but clang's C++1z implementation status notes this as a C++1z proposal which is not implemented.
So if this is a C++14 defect, that would make gcc correct but it is not clear to me if this is really a defect or a proposal.
T.C. points out in a comment here that it seems like the clang developers do intended to back-port this. It has not happened and it is not clear why.
consider the below minimal example.
#include<iostream>
struct A
{
A(){std::cout<<"def"<<'\n';}
void foo()&{std::cout<<"called on lvalue"<<'\n';}
};
int main()
{
A a;
a.foo();
A().foo();
return 0;
}
this gives error about expecting ';' at the end of declaration and and expected un-qualified-id before '{'.
Can i know what i'm doing wrong? in the actual code i want to avoid calling the non-static member function through temporaries.
tried on gcc 4.7.2 and vc2010.
Thanks.
The answer will be short: VC10 and GCC 4.7.2 do not support ref-qualifiers.
However, notice that your foo() function has an lvalue ref qualifier, meaning that you cannot invoke it on temporaries.
If you want this expression to compile as well:
A().foo();
Then you should use const&, or provide an overload for &&, as in this live example.
To work with ref-qualifiers you can use Clang 3.2 or GCC 4.8.1.