Different Compiler Behavior for Expression: Auto P {Make_Pointer()};

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

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.

Different compiler behavior with C++11

The difference is that on Linux, you're using libstdc++ and glibc, and on MacOS you're using libc++ and whatever CRT MacOS uses.

The MacOS version is correct. (Also, your workaround is completely broken and insanely dangerous.)

Here's what I think happens.

There are multiple overloads of conj in the environment. C++98 brings in a single template, which takes a std::complex<F> and returns the same type. Because this template needs F to be deduced, it doesn't work when calling conj with a simple floating point number, so C++11 added overloads of conj which take float, double and long double, and return the appropriate std::complex instantiation.

Then there's a global function from the C99 library, ::conj, which takes a C99 double complex and returns the same.

libstdc++ doesn't yet provide the new C++11 conj overloads, as far as I can see. The C++ version of conj isn't called. It appears, however, that somehow ::conj found its way into the std namespace, and gets called. The double you pass is implicitly converted to a double complex by adding a zero imaginary part. conj negates that zero. The result double complex is implicitly converted back to a double by discarding the imaginary component. (Yes, that's an implicit conversion in C99. No, I don't know what they were thinking.) The result can be assigned to z.

libc++ provides the new overloads. The one taking a double is chosen. It returns a std::complex<double>. This class has no implicit conversion to double, so the assignment to z gives you an error.

The bottom line is this: your code makes absolutely no sense. A vector<double> isn't a vector<complex<double>> and shouldn't be treated as one. Calling conj on double doesn't make sense. Either it doesn't compile, or it's a no-op. (libc++'s conj(double) is in fact implemented by simply constructing a complex<double> with a zero imaginary part.) And wildly reinterpret_casting your way around compile errors is horrible.

why this variable isn't deduced as initializer_list in g++ in C++14?

There is a proposal for C++1z that implements new type deduction rules for brace initialization

Not exactly. If you follow the link to the actual paper, it reads:

Direction from EWG is that we consider this a defect in C++14.

Which is enough to get implementors to also treat it as a defect, and hence change the compiler behaviour even in C++14 mode.

C++11: when does compiler consider {} as std::initializer_list, and when doesn't?

  1. In 'A a={1,'@'}'' :If {} is deduced as std::initializer_list, then how it's converted from std::initilizer_list to type A?

No, it has nothing to do with std::initializer_list. a is just copy-list-initialized from {1,'@'}.


  1. In 'f({1,'#'});' When f requires a type A, does compiler implicitly generates an A object, or it converts from std::initializer_list to A?

It still has nothing to do with std::initializer_list. The function parameter is copy-list-initialized from {1,'#'}.


  1. why when "g()" is a template, the template type deduction doesn't work to give a type A? Does "forward" help to convey the message from f to g, say that T is type A()?

Because this belongs to non deduced context, the template parameter T can't be deduced and cause the compile error.

6) The parameter P, whose A is a braced-init-list, but P is not std::initializer_list or a reference to one:

And

when does compiler consider {} as std::initializer_list, and when doesn't?

When you declare the funciton parameter type as std::initializer_list and you pass a braced list as argument, then std::initializer_list will be constructed. Another case is for auto, e.g.

auto x1 = {3}; // x1 is deduced as std::initializer_list<int>
auto x2{1, 2}; // after C++17, error: not a single element
// before C++17, it was std::initializer_list<int>
auto x3{3}; // after C++17, x3 is deduces as int
// before C++17 it was std::initializer_list<int>

BTW: Even with auto it won't work for {1,'@'}, should it be std::initializer_list<char>, or std::initializer_list<int>?

auto and brace initialization in C++11/C++14

This is proposed change to the C++17 specification - N3922 (I'm not sure if it has been accepted yet).

Basically this presentation from Scott Meyers, slide 20 covers the new rules.

auto var1 {2} ;

Here, var1 will be deduced to be an int.

It does look like some compilers have already implemented the change. I believe the change is more "intuitive" but your mileage may vary. I think in this interim phase, prefer the = initialisation, it may be more portable.

The answer here has some more detail on the history of the proposals and defects raised.

size of variable after auto initialization

Using "typeid" with gcc 4.8.4 as follows:

#include <iostream>
#include <math.h>
#include <typeinfo>

using namespace std;

int main() {
int i{100};
float f{3.14};
double d{3.14159};
cout<<"size of int is: " <<sizeof(i)<<endl;
cout<<"size of float is: " <<sizeof(f)<<endl;
cout<<"size of double is: "<<sizeof(d)<<endl<<endl;;

auto x = sin(3.14159);
cout<<"size of auto that is double is: "<<sizeof(x)<<endl<<endl;

auto y{sin(3.14159)};
cout<<"size of auto that is double is: "<<sizeof(y)<<endl;

cout << typeid(y).name() << endl;

return 0;
}

I get the following output:

$ ./main 
size of int is: 4
size of float is: 4
size of double is: 8

size of auto that is double is: 8

size of auto that is double is: 16
St16initializer_listIdE

I think that the "auto y" is not actually being assigned double, but one of these:
http://en.cppreference.com/w/cpp/utility/initializer_list

It says "An object of type std::initializer_list is a lightweight proxy object that provides access to an array of objects of type const T. "

So most likely the extra space is used to store a pointer and the size of the vector, or something like this.

Why does auto x{3} deduce an initializer_list?

To make long story short:

  • a braced initializer expression {} has no type by itself
  • auto has to infer type information
  • int{3} obviously means "create an int var with value taken from initializer list", thus its type is just int and can be used in any wider context (int i = int{3} will work and auto i = int{3} can deduce type, because right side is obviously of type int)
  • {3} by itself has no type (it can't be int, because it's not a value but an initializer list), so auto wouldn't work — but, because committee considered that auto should still work in this case, they decided that the "best" type for (yeah, typeless by definition) initializer list would be... std::initializer_list, as you already probably guessed.

But, as you pointed out, this made the whole behaviour of auto quite semantically inconsistent. That's why there were proposals to change it — namely N3681, N3912 and N3922 — submitted to the committee. Former proposal was REJECTED as FI3 due to no committee consensus on this matter, http://isocpp.org/files/papers/n3852.html#FI3 , current (N3922) got adopted ca. Q1 of 2015;

tl;dr you may assume that standards-compliant compilers1 with bleeding-edge C++ support2 either have the new, more sane-ish semantics already in place, or will have it shortly.

The Standardization Committee acknowledged the problem by adopting N3922 into draft C++17.

— so it's

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
auto x3{ 1, 2 }; // error: not a single element
auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int>
auto x5{ 3 }; // decltype(x5) is int

now, for better or worse.

further reading:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3681.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3912.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html

http://scottmeyers.blogspot.com/2014/03/if-braced-initializers-have-no-type-why.html

http://herbsutter.com/2014/11/24/updates-to-my-trip-report/


1GCC 5.1 (& up) apparently uses N3922 even in C++11/C++14 mode

2Clang 3.8, with the caveat

This is a backwards-incompatible change that is applied to all language versions that allow type deduction from auto (per the request of the C++ committee).



Related Topics



Leave a reply



Submit