【问题标题】:Does C++ language enforce compiler optimization when using list initialization?使用列表初始化时,C++ 语言是否强制编译器优化?
【发布时间】:2016-08-04 01:26:47
【问题描述】:

这里是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;
}

使用clang++ -std=c++14 -Wall -fno-elide-constructors -pedantic -o main.exe source.cpp 编译上述代码(这里,我禁用了构造优化。顺便说一句,我使用的是 Clang 3.8.1)。然后,我得到以下输出:

#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()

令我惊讶的是#3 并没有像#1 那样先调用A::A(int),然后调用A::A(A &amp;&amp;),尽管它们都是复制初始化的。我还用 gcc 6.1.0 对其进行了测试。同样的事情也会发生。据我所知,列表初始化的一种常见用法是禁止缩小转换。我不知道它与编译优化有关。所以,

C++ 语言在使用列表初始化时是否强制编译器优化,或者只是编译器更喜欢这样做,或者其他原因导致上述行为?

【问题讨论】:

    标签: c++ gcc optimization clang list-initialization


    【解决方案1】:

    在这种情况下,直接初始化和复制列表初始化都会导致调用构造函数。

    在[dcl.init.list] / 3中的规则中使用消除过程,

    T 类型的对象或引用的列表初始化定义如下:

    [...]

    否则,如果T 是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决议(13.3、13.3.1.7)选择最佳构造函数。如果需要缩小转换(见下文)来转换任何参数,则程序格式错误。

    重要的是,复制初始化和复制列表初始化是不等价的,例如,您可以使用复制列表初始化来初始化具有已删除副本和移动构造函数的对象:

    struct A
    {
      A(int i){}
      A(A const&) =delete;
      A(A&&) =delete;
    };    
    
    int main()
    {
      A a1 = {1};
      A a2 = 1; // won't compile
    }
    

    【讨论】:

    • 需要注意的是,direct-list-initialization和copy-list-initialization的唯一区别是后者不会调用explicit构造函数。
    • @NicolBolas 我没有检查过,但 C++17 的强制复制省略规则也可能不同。
    • 没有。保证复制省略通过声明用于初始化该类型值的纯右值不会创建临时值来起作用。一个花括号初始化列表本身 never 会创建一个临时的。所以规则不会直接交互。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    • 2012-05-30
    • 2015-01-11
    • 1970-01-01
    • 2015-06-16
    • 1970-01-01
    • 2021-07-31
    相关资源
    最近更新 更多