8

这段代码,其中有一个const A& a成员B,其中A有一个已删除的复制构造函数,在 GCC 4.8.1 中无法编译,但在 clang 3.4 中可以正常工作:

class A {
public:
    A() = default;
    A(const A&) = delete;
    A& operator=(const A&) = delete;
};

class B{
public:
    B(const A& a)
        : a{a}
    { }
private:
    const A& a;
};

int main()
{
    A a{};
    B b{a};
}

哪一个编译器是正确的?

GCC 中的错误是:

prog.cpp: In constructor ‘B::B(const A&)’:
prog.cpp:11:14: error: use of deleted function ‘A::A(const A&)’
        : a{a}
            ^
prog.cpp:4:5: error: declared here
    A(const A&) = delete;
    ^

ideone:http: //ideone.com/x1CVwx

4

1 回答 1

9

您的示例可以简化为

class A {
public:
    A() = default;
    A(const A&) = delete;
    A& operator=(const A&) = delete;
};

int main()
{
  A a{};
  A const& ar1(a); 
  A const& ar2{a}; // fails on gcc 4.8
}

ar2在 gcc-4.8 上初始化失败并出现错误

error: use of deleted function ‘A::A(const A&)’

它在 clang3.4 和 gcc4.9 上编译干净。这是 CWG问题 1288决议的结果。

N3337 包含以下用于列表初始化的语言:

§8.5.4/3 [dcl.init.list]

类型的对象或引用的列表初始化T定义如下:
...
— 否则,如果T是引用类型,则引用类型的纯右值临时 T被列表初始化,并且引用绑定到该临时

当然,这意味着初始化ar2需要一个可访问的复制构造函数,因此会出现错误。


语言在 N3797 中发生了变化,其中包含单个元素的初始化列表的初始化优先于上面引用的情况。

— 否则,如果初始化列表有一个类型的元素,E并且要么T不是引用类型,要么它的引用类型与引用相关E,则从该元素初始化对象或引用;...
— 否则,如果T是引用类型,则所引用类型的纯右值临时T是复制列表初始化或直接列表初始化,具体取决于引用的初始化类型,并且引用绑定到该临时。

因此 gcc 4.9 和 clang 3.4 正在执行 issue 1288 的解决方案,而 gcc 4.8 则遵循 C++11 标准中的措辞。

于 2014-07-17T04:23:47.817 回答