将std :: shared_ptr与clang ++和libstdc ++一起使用

时间:2011-11-01 09:06:31

标签: c++ shared-ptr clang libstdc++

我正在尝试使用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。)?有人如何解决这个问题?

2 个答案:

答案 0 :(得分:20)

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

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

GCC 4.6.x没有实现这个规则,这个规则在N3203=10-0193过程中很晚就进入了C ++ 11工作文件。 libstdc ++ 4.6.x中的shared_ptr在编写时是正确的,但C ++ 11在此之后发生了变化.. Boost的exactly the same issue带有shared_ptr,这是GCC之间的common incompatibilities之一和Clang。

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

答案 1 :(得分:6)

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

shared_ptr(const shared_ptr& r) noexcept;

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

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

但是没有合适的拷贝构造函数(编译器不能将模板化构造函数用作拷贝构造函数)。不过,我觉得奇怪的是,这样的错误会产生一个生产编译器......

编辑我一直试图找出为什么g ++ 4.6使用它自己的标准库编译上面的代码。它似乎正在将template d构造函数作为复制构造的可行重载,这使我回顾标准来验证这是否是一个错误 - 我一直认为模板的印象不能用作复制构造函数 - 或者不能。

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

  

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

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

  

12.8 / 3如果类X的构造函数的第一个参数是类型(可选择cv-qualified)X并且没有其他参数或者所有其他参数都有默认参数,那么它的构造函数声明是错误的。 永远不会实例化成员函数模板,以便将类对象的副本复制到其类类型的对象。

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

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

  

12.8 / 6如果类X的构造函数的第一个参数是类型(可选择cv-qualified)X并且没有其他参数或者所有其他参数都有默认参数,那么它的构造函数声明是错误的。 永远不会实例化成员函数模板以生成此类构造函数签名。

现在,这意味着限制模板不能用于复制已被删除,并被较不严格的限制所取代:模板将不会立即生成以构建一个表单S( S ),即可能导致复制构造函数模糊的表单。

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

现在接下来的问题是g ++ 4.6附带的标准库实现是否真的符合要求。我不能说我的头脑。一方面,它缺少我上面提到的构造函数的签名,所以你可以说它不符合。但另一方面,兼容的编译器应该选择模板化的构造函数来实现相同的功能,并且实现将表现为构造函数存在的 as-if