将 std::shared_ptr 与 clang++ 一起使用和 libstdc++
我正在尝试使用 libstdc++(4.6.1) 在 clang++(clang version 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.) 一起编译?有人对如何解决这个问题有什么想法吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
根据 C++11 12.8p7,shared_ptr 的隐式声明的复制构造函数被删除,因为共享构造函数具有移动构造函数或移动赋值运算符(或两者):
如果类定义未显式声明复制构造函数,则隐式声明一个复制构造函数。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为已删除;否则,它被定义为默认(8.4)。
GCC 4.6.x 没有实现此规则,该规则在此过程中很晚才进入 C++11 工作文件 N3203=10-0193。 libstdc++ 4.6.x中的shared_ptr在编写时是正确的,但C++11在那之后发生了变化。Boost有与它的shared_ptr 完全相同的问题,这是GCC 和 Clang 之间的常见不兼容性。
向shared_ptr添加默认的复制构造函数和复制赋值运算符将解决该问题。
The implicitly-declared copy constructor for shared_ptr is deleted because shared_ptr has a move constructor or a move assignment operator (or both), per C++11 12.8p7:
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4).
GCC 4.6.x does not implement this rule, which came into the C++11 working paper very late in the process as N3203=10-0193. The shared_ptr in libstdc++ 4.6.x was correct at the time it was written, but C++11 changed after that.. Boost had exactly the same issue with it's shared_ptr, and this is one of the common incompatibilities between GCC and Clang.
Adding a defaulted copy constructor and copy assignment operator to shared_ptr will fix the problem.
在这种情况下,gcc 4.6 的标准库头似乎是错误的,因为标准需要以下构造函数(第 20.7.2.2.1/16):
这是一个复制构造函数,gcc 实现中似乎缺少该构造函数。我手头的实现(g++-4.6.0)提供了(在bits/shared_ptr.h中):
但没有适当的复制构造函数(编译器不能将模板化构造函数用作复制构造函数)。不过,我觉得很奇怪,生产编译器会出现这样的错误...
编辑 我一直在试图弄清楚为什么 g++ 4.6 使用它自己的标准库编译上述代码。似乎它正在选择模板构造函数作为复制构造的可行重载,这让我回顾标准来验证这是否是一个错误——我一直有这样的印象模板不能用作复制构造函数——或者不能。
在C++03标准中,有一个脚注106)
C++11 中不存在该脚注。脚注不规范,因此必须有其他引用来支持该注释。您可以在下面找到几段:
该段落的第一部分意味着构造函数不能采用与参数相同的类型(复制构造函数接受引用,并允许这样的构造函数会导致歧义,并且事实上它需要复制到参数中,对此最好的重载(假设这会抑制复制构造函数)将是相同的功能,这又需要......无限 环形)。
该条款的第二部分指出模板化构造函数不能用于创建类型的副本,这似乎是支持脚注106)的规范部分。现在,这已在 C++11 中仔细改写为:
现在,这意味着模板不能用于复制的限制已被删除并替换受不太严格的限制:模板不会被实例化以生成
S( S )
形式的构造函数,即会导致复制构造函数产生歧义的构造函数。有了这个较小的限制,上面的模板化构造函数似乎实际上可以用作复制构造函数,因为它生成的签名是兼容的。这支持 g++ 4.6 编译器在处理
bits/shared_ptr.h
标头时的行为,并且意味着clang++
无法使用模板作为有效的构造函数。现在下一个问题是 g++4.6 附带的标准库实现是否实际上兼容。我不能凭空说出来。一方面,它缺少我上面提到的构造函数的签名,因此您可以认为它不兼容。但另一方面,兼容的编译器应该选择模板化构造函数来实现相同的功能,并且实现将表现得就像构造函数存在一样。
The standard library headers for gcc 4.6 seem to be wrong in this case as the standard requires the following constructor (§20.7.2.2.1/16):
Which is a copy-constructor that seem to be missing from the gcc implementation. The implementation I have at hand (g++-4.6.0) offers (in
bits/shared_ptr.h
):But does not have a proper copy-constructor (a templated constructor cannot be used by the compiler as a copy constructor). I find it strange, though, that such an error would come up with a production compiler...
EDIT I have been trying to figure out why exactly g++ 4.6 compiles the above code with it's own standard library. It seems that it is picking up the
template
d constructor as a viable overload for copy construction, which made me look back to the standard to verify whether that is a bug --I have always been under the impression that a template could not been used as a copy constructor-- or not.In the C++03 standard, there is a footnote 106)
That footnote is not present in C++11. Footnotes are not normative, so there has to be other quote supporting that note. A couple of paragraphs below there you can find:
The first part of the paragraph means that a constructor cannot take the same type as argument (copy constructors take references, and allowing such constructor would cause an ambiguity, that and the fact that it would require copying into the argument, for which the best overload --assuming that this inhibited the copy constructor-- would be the same function, which in turns would require... infinite loop).
The second part of the clause states that a templated constructor cannot be used to create copies of the type, which seems to be the normative section supporting footnote 106). Now, that has been carefully reworded in C++11 to state:
Now, what this means is that the restriction a template cannot be used to copy has been removed, and replaced by the less strict restriction: a template will not be instantitated to generate a constructor of the form
S( S )
, i.e. one that would cause an ambiguity with the copy constructor.With this lesser restriction, it seems that the templated constructor above can actually be used as a copy-constructor, as the signature that it generates is compatible. This supports the behavior of the g++ 4.6 compiler when processing the
bits/shared_ptr.h
header, and would imply thatclang++
is failing to use the template as a valid constructor.Now the next question is whether the standard library implementation shipped with g++4.6 is actually compliant or not. I cannot say off the top of my head. On one side, it is missing the signature of constructor that I mentioned above, so you can argue that it is not compliant. But on the other hand, a compliant compiler should pick up the templated constructor to achieve the same functionality, and the implementation would behave as-if that constructor was present.