Different compiler behavior for expression: auto p {make_pointer()}; - c++

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.

Related

The C++17 compiler (gcc or Microsoft Visual C++), does it have an option that prohibit the feature "not produce a temporary"

How can I told to С++17 compiler to create temporary in the following case
(i.e. The C++17 compiler must considered copy/move operation, like C++11 and C++14 compilers do)
class A{
public:
A(){}
A(const A&)=delete;
A(A&&)=delete;
};
A f(){
return A();
}
int main(){
auto a=f();
}
Output (c++14 - this is what I want):
gcc -std=c++14 ./a.cpp
./a.cpp: In function ‘A f()’:
./a.cpp:8:11: error: use of deleted function ‘A::A(A&&)’
return A();
^
./a.cpp:5:3: note: declared here
A(A&&)=delete;
^
./a.cpp: In function ‘int main()’:
./a.cpp:11:11: error: use of deleted function ‘A::A(A&&)’
auto a=f();
^
./a.cpp:5:3: note: declared here
A(A&&)=delete;
^
Output (c++17):
gcc -std=c++17 ./a.cpp
./a.out
(successfully run)
My Customer have a ton of C++11 code and C++17 compilers: gcc 8.3.0, and Microsoft C++ compilers (from Visual Studio 2017 and 2019).
And there are many places that contain the code above.
The C++17 compiler, does it have an option that prohibit the feature "not produce a temporary" in such a case ?
You cannot (and should not) deactivate guaranteed elision. However, if you want to prevent it in a specific case, all you need to do is not use a prvalue:
A f(){
A a{};
return a;
}
That will require that A can be moved, though the move can (and almost certainly will) still be elided.
One of the main points of guaranteed elision is that you now can return prvalues of an immoveable type. This allows you to do more complex initialization through factory functions. This is not a thing that you should want to prevent globally.

undefined reference to class static constexpr struct, g++ vs clang

This is my code, a.cpp
struct int2
{
int x, y;
};
struct Foo{
static constexpr int bar1 = 1;
static constexpr int2 bar2 = {1, 2};
};
int foo1(){
return Foo::bar1; // this is ok for both clang++ and g++
}
int2 foo2(){
return Foo::bar2; // undefined reference to `Foo::bar2' in clang++
}
int main(){ std::cout << foo2().x << std::endl; return 0; }
use clang to compile, clang++ -std=c++11 a.cpp
/tmp/a-0dba90.o: In function `foo2()':
a.cpp:(.text+0x18): undefined reference to `Foo::bar2'
clang-7: error: linker command failed with exit code 1 (use -v to see
invocation)
g++ -std=c++11 a.cpp emits no error.
My question is,
who is right on the above code? clang or g++?
why bar2 is wrong while bar1 is correct in clang?
compiler version: g++ 5.4.0 and clang 7.0.0
UPDATE: The question is marked as a duplicate of another question, but it is not. I know I could add explicitly definition outside class to get it pass for clang. This question is about why all the difference between g++&clang.
You seem to assume that if one compiler is right, the other one must be wrong. The program either contains an error (and then the compiler that accepts it is wrong) or it doesn't (and then the compiler that rejects it is wrong). This in turn relies on an implicit assumption that the error in question, namely, missing definition of an ODR-used entity, is a diagnosable error. Unfortunately it isn't. The standard explicitly states that:
[basic.def.odr/10] Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement; no diagnostic required.
As problematic and unwanted this provision of the standard is, it's there. Your program has undefined behaviour because of a missing definition, and an implementation is not required to diagnose it. So both compilers are technically correct at any optimisation level.
In C++17 with mandatory copy elision the program no longer contains any ODR-use of the variable in question constexpr static data members are implicitly inline, no separate deinition needed (thanks Oliv).
Answer my own question.
I have some vague understanding about odr-use.
For literal type Foo::bar1, it is not odr-used, so it is fine.
For struct Foo::bar2: when return a struct inside a function body, it will invoke its copy constructor, which take a reference to Foo::bar2. So Foo::bar2 is odr-used, its definition must exist somewhere in the program otherwise it will result a link error.
But why g++ does not complain? I guess it is related to compiler optimization.
Verify my guess:
copy elision
add -fno-elide-constructors, g++ -fno-elide-constructors -std=c++11 a.cpp
/tmp/ccg1z4V9.o: In function foo2()':
a.cpp:(.text+0x27): undefined reference toFoo::bar2'
So, yes, copy elision will affect this.
But g++ -O1 still get passed.
function inline
add -fno-line, g++ -O1 -fno-elide-constructors -fno-inline -std=c++11 a.cpp
/tmp/ccH8dguG.o: In function foo2()':
a.cpp:(.text+0x4f): undefined reference toFoo::bar2'
Conclusion is both copy elision & function inline will affect its behavior. The different between g++ & clang is because g++ enabled copy elision by default but clang does not.

Explicitly defaulted special member functions with aliased type — Clang vs. GCC

GCC trunk (9.0) and Clang trunk (7.0) disagree on the following code:
template<int size_>
struct BadArray {
static constexpr int size = size_;// GCC complains due to this indirection
using Self = BadArray<size>;
Self& operator=(const Self&) noexcept = default;
};
Compilation with GCC fails with the error message
error: 'BadArray<size_>::Self& BadArray<size_>::operator=(const Self&)' cannot be defaulted
while Clang accepts the code (live example with GCC 8.1 and Clang 6.0).
Is there a well-defined behavior? If yes, which compiler is right?
Is it appropriate to file a bug report for either GCC or Clang?

if constexpr in a recursive generic lambda: different compiler behavior

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.

C++11 lambda doesn't take const variable by reference, why?

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.