【问题标题】:Is clang Xcode 4.4.1 buggy when -fno-elide-constructors is set?设置 -fno-elide-constructors 时,clang Xcode 4.4.1 是否存在问题?
【发布时间】:2012-08-27 19:29:53
【问题描述】:

我正在尝试自学移动构造函数和移动赋值,以便让我的学生开始使用 C++11 的这一特性。我已经看到(并在本网站的其他地方进行了解释)编译器会在大多数情况下优化移动(或普通)构造,因此您无法在工作中看到它。使用 gcc 4.7.0,-fno-elide-constructors 选项将关闭它,您可以看到移动构造正在发生。但是同样的标志应该(?)是 clang 选项,当我在 Xcode 4.4.1 中使用 clang、c++11 和 stdc++ 将其指定为“其他 C++ 标志”时,显然不会复制返回值或者根本搬出去!

这里是完整的测试代码和带有问题发生位置注释的输出:

#include <iostream>
using namespace std;

class Array_Exception {
public:
Array_Exception (int v, const char * msg) :
    value (v), msg_ptr(msg) {}

int value;
const char * msg_ptr;
};

class Array {
public:
Array() : size(0), ptr(nullptr), serial_number(++counter) {
    cout << "Array " << serial_number << " default constructed";
    }

Array(int size_) : size(size_), serial_number(++counter) {
    if(size <= 0)
        throw Array_Exception(size, "size must be greater than 0");
    ptr = new int[size];
    cout << "Array " << serial_number << " constructed" << endl;
    }

Array(const Array& source) : size(source.size), ptr(new int[size]), serial_number(++counter) {
    for (int i = 0; i < size; i++)
        ptr[i] = source.ptr[i];
    cout << "Array " << serial_number << " constructed from " << source.serial_number << endl;
    }

// move constructor - take the guts from the source and leave it in a safely destructable state
Array(Array&& source) : size(source.size), ptr(source.ptr), serial_number(++counter) {
    source.size = 0;
    source.ptr = nullptr;
    cout << "Array " << serial_number << " move constructed from " << source.serial_number << endl;
    }

// copy the data from rhs into lhs object using copy-swap
Array& operator= (const Array& source) {
    Array temp(source);
    swap(temp);
    cout << "Array " << serial_number << " assigned from " << source.serial_number << endl;
    return *this;
    }

// move assignment just swaps source with this.
Array& operator= (Array&& source) {
    swap(source);
    cout << "Array " << serial_number << " move assigned from " << source.serial_number << endl; 
    return *this;
    }

// swap the member variable values of this object with the other (serial_numbers are NOT swapped)
void swap(Array& other) {
    int t_size = size;  // could use std::swap
    size = other.size;
    other.size = t_size;
    int * t_ptr = ptr;
    ptr = other.ptr;
    other.ptr = t_ptr;
    }       

~Array() {
    delete[] ptr; 
    cout << "Array " << serial_number << " destroyed" << endl;
    }   

int get_size() const {return size;}

// overloaded plus operator returns an Array containing the sum of the two
Array operator+ (const Array& rhs) { // "this" object is lhs
    // must have the same size
    if(size != rhs.get_size())
        throw Array_Exception(size, "LHS and RHS must have the same size for +");
    Array result(size);
    for(int i = 0; i < size; i++)
        result[i] = ptr[i]+rhs[i];
    return result;
    }

const int& operator[] (int index) const {
    if ( index < 0 || index > size - 1) {
        throw Array_Exception(index, "Index out of range");
        }
    return ptr[index];
    }

int& operator[] (int index) {
    if ( index < 0 || index > size - 1) {
        throw Array_Exception(index, "Index out of range");
        }
    return ptr[index];
    }

private:
int size;
int* ptr;
int serial_number;
static int counter; 
};

int Array::counter = 0;

void print_all(const char * msg, const Array& a);
Array reverse_contents(Array a); // call and return by value

int main()
{   

Array a(5), b(5), c(5);
for (int i = 0; i < 5; i++)
    a[i] = i;   // 0, 1, 2, 3, 4

try {            
    cout << "\ncall by value and assign from returned value:" << endl;
    print_all("a has", a);
    b = reverse_contents(a);
    print_all("b has reversed", b);

    cout << "\nassign from rvalue expression" << endl;
    c = a + b;
    print_all("c has sum", c);

    cout << "\nconstruct from rvalue expression" << endl;
    Array e(a + c);
    print_all("e has copy of a + c", e);

    cout << "\nconstruct from function return value" << endl;
    Array d(reverse_contents(a));
    print_all("d has returned reverse of a", d);

   }

catch (Array_Exception& x) {
    cout << x.msg_ptr << ' ' << x.value << endl;
    }

cout << "\nDone!" << endl;
}

void print_all(const char * msg, const Array& a) {
cout << msg << ": ";
for(int i= 0; i < a.get_size(); i++)
    cout << a[i] << ' ';
cout << endl;
}

Array reverse_contents(Array a) {
int n = a.get_size();
Array result(n);

for(int i = 0; i < n; i++) {
    result[i] = a[n-i-1];
    }
return result; 
}

使用 clang 和 -fno-elide 构造函数运行它并没有走得太远:被调用函数中的局部变量在返回期间被销毁,我们最终从一个虚假对象进行了移动赋值:

Array 1 constructed
Array 2 constructed
Array 3 constructed

call by value and assign from returned value:
a has: 0 1 2 3 4 
Array 4 constructed from 1
Array 5 constructed
Array 5 destroyed  << local Array has been destructed
Array 2 move assigned from 0  << Array with serial_number == 0 is bogus object
Array 0 destroyed << bogus object destroyed here
Array 4 destroyed
b has reversed: << nothing in the returned result

程序在下一个测试中停止,因为 b 中没有任何内容。

在 gcc 4.7.0 下运行的相同代码似乎很有意义,并显示了移动构造在工作中: g++ -std=c++11 *.cpp -fno-elide-constructors

Array 1 constructed
Array 2 constructed
Array 3 constructed

call by value and assign from returned value:
a has: 0 1 2 3 4 
Array 4 constructed from 1
Array 5 constructed
Array 6 move constructed from 5
Array 5 destroyed
Array 2 move assigned from 6
Array 6 destroyed
Array 4 destroyed
b has reversed: 4 3 2 1 0 

assign from rvalue expression
Array 7 constructed
Array 8 move constructed from 7
Array 7 destroyed
Array 3 move assigned from 8
Array 8 destroyed
c has sum: 4 4 4 4 4 

construct from rvalue expression
Array 9 constructed
Array 10 move constructed from 9
Array 9 destroyed
Array 11 move constructed from 10
Array 10 destroyed
e has copy of a + c: 4 5 6 7 8 

construct from function return value
Array 12 constructed from 1
Array 13 constructed
Array 14 move constructed from 13
Array 13 destroyed
Array 15 move constructed from 14
Array 14 destroyed
Array 12 destroyed
d has returned reverse of a: 4 3 2 1 0 
Array 15 destroyed
Array 11 destroyed

Done!
Array 3 destroyed
Array 2 destroyed
Array 1 destroyed

因此,在 gcc 4.7 中关闭 elision 会产生合理的结果,但会在 clang 中产生损坏的代码。这是clang中的错误,还是clang实际上不支持该编译器选项?

【问题讨论】:

    标签: c++ xcode c++11 clang move-semantics


    【解决方案1】:

    这看起来像这个 clang 错误:

    http://llvm.org/bugs/show_bug.cgi?id=12208

    【讨论】:

    • 谢谢!由于某种原因,我的搜索没有找到。下次我会单独看llvm bug list!至少 gcc 4.7 有效,这将有助于教学目的。
    猜你喜欢
    • 1970-01-01
    • 2015-01-21
    • 2014-01-30
    • 2022-12-14
    • 2020-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    相关资源
    最近更新 更多