12

我正在尝试使用 libstdc++(4.6.1) 在 clang++(clang 版本 3.1 (trunk 143100)) 中使用 std::shared_ptr。我有一个小演示程序:

#include <memory>

int main()
{
    std::shared_ptr<int> some(new int);
    std::shared_ptr<int> other(some);
    return 0;
}

可以使用以下方法构建:

clang++ -std=c++0x -o main main.cpp

并给出以下错误输出:

main.cpp:6:23: error: call to deleted constructor of 'std::shared_ptr<int>'
    std::shared_ptr<int> other(some);
                         ^     ~~~~
/usr/include/c++/4.6/bits/shared_ptr.h:93:11: note: function has been explicitly marked
deleted here
class shared_ptr : public __shared_ptr<_Tp>

由于某种原因,它需要被删除的构造函数,因为提供了移动构造函数(这是正确的行为)。但是为什么它可以与 (g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1.) 一起编译?有人对如何解决这个问题有任何想法吗?

4

2 回答 2

20

根据 C++11 12.8p7,shared_ptr 的隐式声明的复制构造函数被删除,因为 shared_ptr 具有移动构造函数或移动赋值运算符(或两者):

如果类定义没有显式声明复制构造函数,则隐式声明。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数定义为已删除;否则,它被定义为默认值(8.4)。

GCC 4.6.x 没有实现此规则,该规则在该过程的后期以N3203=10-0193的形式出现在 C++11 工作文件中。libstdc++ 4.6.x 中的 shared_ptr 在编写时是正确的,但此后 C++11 发生了变化。Boost 的 shared_ptr 具有完全相同的问题,这是GCC 和 Clang 之间常见的不兼容问题之一。

将默认的复制构造函数和复制赋值运算符添加到 shared_ptr 将解决问题。

于 2011-11-01T14:36:08.073 回答
6

在这种情况下,gcc 4.6 的标准库头文件似乎是错误的,因为标准需要以下构造函数(第 20.7.2.2.1/16 节):

shared_ptr(const shared_ptr& r) noexcept;

这是 gcc 实现中似乎缺少的复制构造函数。我手头的实现(g++-4.6.0)提供(in bits/shared_ptr.h):

template<typename _Tp1>
shared_ptr(const shared_ptr<_Tp1>& __r)

但没有适当的复制构造函数(编译器不能将模板化构造函数用作复制构造函数)。不过,我觉得很奇怪,生产编译器会出现这样的错误......

编辑我一直试图弄清楚为什么 g++ 4.6 使用它自己的标准库来编译上面的代码。似乎它正在选择templated 构造函数作为复制构造的可行重载,这让我回顾标准以验证这是否是一个错误——我一直认为模板不能用作一个复制构造函数——或者不是。

在 C++03 标准中,有一个脚注106)

因为模板构造函数永远不是复制构造函数,所以这样的模板的存在不会抑制复制构造函数的隐式声明。模板构造函数与其他构造函数(包括复制构造函数)一起参与重载决议,如果模板构造函数提供比其他构造函数更好的匹配,则可以使用模板构造函数来复制对象。

该脚注在 C++11 中不存在。脚注不是规范性的,因此必须有其他引用支持该注释。您可以在下面找到几段:

12.8/3 如果类 X 的第一个参数是类型(可选 cv 限定)X 并且没有其他参数或者所有其他参数都具有默认参数,则类 X 的构造函数声明是格式错误的。永远不会实例化成员函数模板来执行将类对象复制到其类类型的对象。

该段的第一部分意味着构造函数不能采用与参数相同的类型(复制构造函数接受引用,并且允许这样的构造函数会导致歧义,而且它需要复制到参数中,为此最好的重载--假设这禁止了复制构造函数--将是相同的函数,而这又需要...无限循环)。

该条款的第二部分声明不能使用模板化构造函数来创建该类型的副本,这似乎是支持脚注106)的规范部分。现在,这已在 C++11 中仔细改写为:

12.8/6 如果类 X 的第一个参数是类型(可选 cv 限定)X 并且没有其他参数或所有其他参数都具有默认参数,则类 X 的构造函数声明是格式错误的。永远不会实例化成员函数模板来生成这样的构造函数签名。

现在,这意味着模板不能用于复制的限制已被删除,取而代之的是不太严格的限制:模板不会被实例化以生成 form 的构造函数S( S ),即会导致与复制构造函数。

有了这个较小的限制,似乎上面的模板化构造函数实际上可以用作复制构造函数,因为它生成的签名是兼容的。这支持 g++ 4.6 编译器在处理bits/shared_ptr.h标头时的行为,并暗示clang++无法将模板用作有效的构造函数。

现在下一个问题是 g++4.6 附带的标准库实现是否真正兼容。我不能说我的头顶。一方面,它缺少我上面提到的构造函数的签名,所以你可以说它不兼容。但另一方面,兼容的编译器应该选择模板化的构造函数来实现相同的功能,并且实现的行为就像该构造函数存在一样。

于 2011-11-01T09:13:28.257 回答