15

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)
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Escualo
  • 40,844
  • 23
  • 87
  • 135
  • 6
    @0x499602D2: That makes no sense. The whole point of a language standard is to unambiguously define program semantics. – Lightness Races in Orbit Jul 08 '15 at 19:07
  • @LightnessRacesinOrbit: While I agree, that it is not the case here, you statement is not true in general: For one, you have - especially in C and C++ - UB and IB, in which case ANY behavior is correct (for IB it just has to be documented). Then you have optional behavior (e.g. copy elision) and finally, even an international standard can contain errors which might result in ambiguous situations. – MikeMB Jul 08 '15 at 19:52
  • 1
    @MikeMB: Feel free to show me an example of when IB leads to compiler error in one implementation but not another. And UB doesn't count, by definition. – Lightness Races in Orbit Jul 08 '15 at 20:14
  • @Lightness: On the contrary: As long as the standard doesn't say the program is ill formed, UB means that any behavior is correct according to the standard. So if two compilers compile the same program that contains UB and they behave differently, then *"both compilers are right"*. Even if it is ill formed, there are the cases of *"ill formed - no diagnostics required"*. As far as IB is concerned: I'm not sure, but as far as I remember, there is an implementation defined maximum template recursion depth. Oh, and what about *Conditionally-Supported Behavior*? Anyway, its not the case here. – MikeMB Jul 08 '15 at 20:38
  • This program specifies that `p` is of type "whatever we need to initialize from a `std::shared_ptr`". That may or may not be a `std::shared_ptr`, the standard neither requires it to be nor prohibits it from being. So you may or may not be able to do `p->Bar();`. The standard does not say which. – David Schwartz Nov 05 '15 at 18:34
  • @LightnessRacesinOrbit `static_assert(sizeof(int) == 4, "hello");` – M.M Nov 05 '15 at 20:18

1 Answers1

16

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.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 4
    Scoot Myers Talks about this [here](https://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Type-Deduction-and-Why-You-Care) at about 30 minutes in. – NathanOliver Jul 08 '15 at 19:13
  • 1
    What's interesting is N3922 calls this a C++14 defect (which is what I thought it was too), but clang [claims](http://clang.llvm.org/cxx_status.html) it's a C++1z change (search for N3922 on that page). gcc-5.1 accepts the example in the OP in C++11 mode also. – Praetorian Jul 08 '15 at 19:19
  • @Praetorian good point, I was wondering about that, I modified my answer to mention that. – Shafik Yaghmour Jul 08 '15 at 19:24
  • @Escualo depending on whether this is considered a C++14 defect or not then gcc may actually be correct. – Shafik Yaghmour Jul 08 '15 at 19:25
  • @hvd I posted my comment before the clarification was added --- I will *not* be filing a GCC bug. – Escualo Jul 08 '15 at 19:25
  • That problem has been around c++11 and is nothing specific to c++14. Actually I thought they wantted to fix it already in c++14 – MikeMB Jul 08 '15 at 19:42
  • http://stackoverflow.com/questions/25612262/why-does-auto-x3-deduce-an-initializer-list – MikeMB Jul 08 '15 at 19:56
  • [P0636](http://wg21.link/p0636r3) describes N3922 as 'a defect resolution against C++14' but does not explicitly mark it as a defect report. It also has a section explaining [defects](http://wg21.link/p0636r3#dr). – John McFarlane May 29 '19 at 19:26