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

Bjo*_*orn 12 c++ clang shared-ptr 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;
}
Run Code Online (Sandbox Code Playgroud)

可以使用以下方法构建:

clang++ -std=c++0x -o main main.cpp
Run Code Online (Sandbox Code Playgroud)

并给出以下错误输出:

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>
Run Code Online (Sandbox Code Playgroud)

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

小智 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 与它的shared_ptr 有完全相同的问题,这是GCC和Clang之间常见的不兼容性之一.

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


Dav*_*eas 6

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

shared_ptr(const shared_ptr& r) noexcept;
Run Code Online (Sandbox Code Playgroud)

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

template<typename _Tp1>
shared_ptr(const shared_ptr<_Tp1>& __r)
Run Code Online (Sandbox Code Playgroud)

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

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

在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附带的标准库实现是否真的符合要求.我不能说我的头脑.一方面,它缺少我上面提到的构造函数的签名,所以你可以说它不符合.但另一方面,兼容的编译器应该选择模板化的构造函数来实现相同的功能,并且实现将表现为 - 如果该构造函数存在.