【问题标题】:Move semantics in MS C++ vs Clang在 MS C++ 与 Clang 中移动语义
【发布时间】:2012-04-09 13:49:10
【问题描述】:

在使用我创建的数组类型对移动语义进行了一些实验之后,我想知道为什么 Microsoft 的 C++ 编译器在从方法按值返回时调用移动构造函数,而 Clang 编译器将副本全部省略?

Clang 的这种行为是正确的还是不正确的?或 Microsoft 的正确行为?

#include <algorithm>
#include <iostream>

template<typename T>
class Array {
    public:
    template<typename E>
    class ArrayIterator {
        public:
        ArrayIterator(Array<E>& elements, int index) : position_(index), elements_(elements) {
        }

        T& operator * () {
            return elements_[position_];
        }

        ArrayIterator& operator++ () {
            position_++;
            return *this;
        }

        ArrayIterator operator++ (int) {
            return ArrayIterator(elements_, ++position_);
        }

        bool operator != (ArrayIterator const & other) {
            return position_ != other.position_;
        }

        private:
        int position_;
        Array<E>& elements_;
    };
    typedef ArrayIterator<T> iterator;
    Array();
    explicit Array(int size);
    ~Array();
    Array(const Array& other);
    Array(Array&& other);
    Array<T>& operator = (Array other);
    T& operator[](int index);
    int size() const;
    iterator begin();
    iterator end();


    private:
    void internal_swap(Array& other);
    T *elements_;
    int length_;
};

template<typename T>
Array<T>::Array() {
    length_ = 0;
    elements_ = 0;
}

template<typename T>
Array<T>::Array(int size) {
    elements_ = new T[size];
    length_ = size;
}

template<typename T>
Array<T>::~Array() {
    delete[] elements_;
    std::cout << "Destroy...." << std::endl;
}

template<typename T>
Array<T>::Array(const Array<T>& other) { 
    std::cout << "copy ctor" << std::endl;

    length_ = other.size();

    T *elements = new T[size()];
    std::copy(other.elements_, other.elements_ + other.size(), elements);

    elements_ = elements;
}

template<typename T>
Array<T>::Array(Array<T>&& other) { 
    std::cout << "move ctor" << std::endl;
    length_ = other.size();
    T* oelements = other.elements_;
    other.elements_ = 0;
    this->elements_ = oelements;

}

template<typename T>
Array<T>& Array<T>::operator = (Array other) {
    internal_swap(other);
    return *this;
}

template<typename T>
T& Array<T>::operator[](int index) {
    return elements_[index];
}

template<typename T>
int Array<T>::size() const {
    return length_;
}

template<typename T>
typename Array<T>::iterator Array<T>::begin() {
    return iterator(*this, 0);
}

template<typename T>
typename Array<T>::iterator Array<T>::end() {
    return iterator(*this, size());
};

template<typename T>
void Array<T>::internal_swap(Array& other){
    T* oelements = other.elements_;
    other.elements_ = this->elements_;
    this->elements_ = oelements;
}

Array<int> get_values(int x);

int main(int argc, const char *argv[]) {

    Array<int> a = get_values(2);

    for (Array<int>::iterator i = a.begin(); i != a.end(); ++i) {
        std::cout << *i << std::endl;
    }

    return 0;
}

Array<int> get_values(int x) { 
    Array<int> a(10);


    if(x == 1) return a;


    for (int i = 0; i <= 9; i++) {
        a[i] = 1 + i;
    }

    return a;
}

【问题讨论】:

  • MSVC 和 Clang 的编译器标志是什么?
  • XCode 4.3 和 VS 2010 的所有默认值。
  • 是的,但是,VS 中的默认设置是调试模式,也就是没有优化......所以让我们说一下有点不同:你是否在发布模式下编译/启用了优化?
  • @Xeo:用 /O2(生成快速代码)和 /GL(整个程序优化)编译仍然输出“move ctor”...
  • 请注意,如果您在 get_values() 函数体中注释掉 if(x == 1) return a; 行,则应用 NRVO。似乎if 语句阻止了 NRVO...

标签: c++ visual-c++ clang visual-c++-2010 move-semantics


【解决方案1】:

复制省略是标准允许不同的可观察行为(它不属于 as-if 规则)但不是未定义行为的罕见优化之一。

在此上下文中是否调用或省略任何复制或移动构造函数是未指定的,不同的编译器可以表现不同并且都是正确的。

【讨论】:

  • 我认为 move ctor 是首选?
  • @Blair:哦,不,在正确的位置开始构建对象绝对比移动它并破坏旧对象更可取。
  • 所以你说的Move只有在RVO/NRVO不能用的情况下才用?即最后的手段?
  • 是的,这就是为什么新标准在以前允许复制省略的相同条件下特别允许移动省略。
  • 非常感谢你,这太棒了。完全解释了当时的行为。 Plus 解释了为什么 NRVO / RVO 比移动语义更受欢迎。
猜你喜欢
  • 2014-08-16
  • 1970-01-01
  • 1970-01-01
  • 2019-01-02
  • 2020-08-29
  • 2021-07-24
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多