【问题标题】:When I initialize a C++ container (such as a std::list) is the copy constructor called?当我初始化 C++ 容器(例如 std::list)时,是否调用了复制构造函数?
【发布时间】:2011-09-29 19:52:33
【问题描述】:

当我使用例如 list< vector<char> > 初始化 STL 容器时my_list.push_back(vector<char>(5000, 'T'))这是在施工后复制的吗?还是编译器会调用 list< vector<char> > 自身内部的构造函数?

【问题讨论】:

    标签: c++ copy-constructor move-constructor


    【解决方案1】:

    在 C++03 中,push_back 定义为 void push_back(const T& x);。这意味着您正在构建一个vector,并且对此类时间的 const 引用被传递给list。然后list 在内部调用复制构造函数以存储此类元素的副本。

    在 C++11 中,void push_back(T&& x); 有一个额外的定义,它采用对您的时间 vector 的右值引用,并会导致内部调用移动构造函数来初始化 list 持有的副本。

    【讨论】:

    • 也就是说,Microsoft Visual C++ 2010 及更高版本不复制?
    • @unixman83:这不是由编译器定义的,而是由标准库的实现定义的。我的理解是 VC++10 实现了右值引用,所以我希望它进行移动构造而不是复制构造。
    • @unixman83: VC++9 肯定会导致复制构造。
    【解决方案2】:

    编译器很聪明。真聪明。在这种情况下,有一种称为“复制省略”的优化。 C++ 标准允许编译器在使用临时对象初始化相同类型的对象时省略副本,并且该对象的复制构造函数没有副作用。

    这与更流行的“好像”规则属于同一类优化。只要生成的程序的可观察行为与“好像”完全遵循标准一样,该规则就允许编译器摆脱几乎任何它想要的行为。

    这是一个示例程序。在带有 -O0 和 -O3 的 gcc 4.4.5 上,此代码导致打印“1”。我认为 GCC 在这里是错误的……一些编译器会输出“2”,表示发生了副本。这就是在尝试检测本应无法检测的行为时事情变得棘手的地方。在其中一个编译器中,唯一的判断方法是深入研究生成的程序集。

    #include <iostream>
    
    struct elision
    {
        explicit elision(int i) : v(i) {
        }
    
        elision(elision const &copy) : v(copy.v+1) {
        }
    
        int v;
    };
    
    int main()
    {
        elision e(elision(1));
        std::cout << e.v << std::endl;
        return 0;
    }
    

    【讨论】:

    • 复制省略是 as-if 规则的唯一例外...编译器不需要考虑复制构造函数的副作用,因此 1 和 2 都是有效输出。
    • 我宁愿谨慎行事,等待移动构造函数成为主流。
    猜你喜欢
    • 1970-01-01
    • 2016-12-29
    • 1970-01-01
    • 1970-01-01
    • 2018-06-30
    • 2020-05-05
    • 2016-05-07
    • 1970-01-01
    • 2015-06-09
    相关资源
    最近更新 更多