Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does C++ language enforce compiler optimization when using list initialization?

Here is the source.cpp

#include <iostream>

struct A {
    A(int i) : i(i) { std::cout << this << ": A(int)" << std::endl; }
    A(A const &a) : i(a.i) { std::cout << this << ": A(A const &)" << std::endl; }
    A(A &&a) : i(a.i) { std::cout << this << ": A(A &&)" << std::endl; }
    ~A() { std::cout << this << ": ~A()" << std::endl; }    

  private:
    int i;
};    

int main() {
    std::cout << "#1 :" << std::endl;
    A a1 = 1; // #1, copy-initialization    

    std::cout << "#2 :" << std::endl;
    A a3(1); // #2, direct-initialization    

    std::cout << "#3 :" << std::endl;
    A a4 = {1}; // #3, copy-list-initialization    

    std::cout << "#4 :" << std::endl;
    A a5{1}; // #4, direct-list-initialization    

    std::cout << std::endl;
    return 0;
}

Compiling above codes with clang++ -std=c++14 -Wall -fno-elide-constructors -pedantic -o main.exe source.cpp (Here, I disable the construction optimization. BTW, I'm using Clang 3.8.1). Then, I get following output:

#1 :
0x61fe40: A(int)
0x61fe48: A(A &&)
0x61fe40: ~A()
#2 :
0x61fe30: A(int)
#3 :
0x61fe28: A(int)
#4 :
0x61fe20: A(int)

0x61fe20: ~A()
0x61fe28: ~A()
0x61fe30: ~A()
0x61fe48: ~A()

What supprise me is #3 doesn't call A::A(int) first and then A::A(A &&) as #1 does, although both of they are copy-initialized. I also tested it with gcc 6.1.0. Same thing happens. As far as I know, one of common usage of list initialization is to prohibit narrowing conversions. I didn't know it has anything related to compiling optimization. So,

Does C++ language enforce compiler optimization when using list initialization or just compilers prefer to do so or something else cause the behavior described above?

like image 821
Carousel Avatar asked Jun 12 '26 18:06

Carousel


1 Answers

Direct- and copy-list-initialization both result in a call to a constructor in this case.

Using a process of elimination in the rules in [dcl.init.list] / 3,

List-initialization of an object or reference of type T is defined as follows:

[...]

Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.

The important thing to take home is that copy-initialization and copy-list-initialization aren't equivalent, you can use copy-list-initialization to initialize an object with deleted copy and move constructors for instance:

struct A
{
  A(int i){}
  A(A const&) =delete;
  A(A&&) =delete;
};    

int main()
{
  A a1 = {1};
  A a2 = 1; // won't compile
}
like image 190
user657267 Avatar answered Jun 16 '26 07:06

user657267



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!