【问题标题】:Using std::shared_ptr with clang++ and libstdc++将 std::shared_ptr 与 clang++ 和 libstdc++ 一起使用
【发布时间】:2011-11-01 09:06:31
【问题描述】:

我正在尝试使用 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.) 一起编译?有人对如何解决这个问题有任何想法吗?

【问题讨论】:

  • /usr/include/c++/4.6 通常不是 G++ 寻找其系统头文件的地方。试试g++ -v yourfile.cpp看看G++的搜索路径。
  • @Mat:不是吗? g++ 通常在 /usr/include/c++/$(version)AFAIK 中查找 c++ 标准头文件,因此 g++4.6 会在该目录中查找。从 clang++ 开始,它可能取决于它是如何编译/配置的

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


【解决方案1】:

根据 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 有 exactly the same issue 和它的 shared_ptr,这是 GCC 和 Clang 之间的 common incompatibilities 之一。

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

【讨论】:

  • 注意:模板构造函数是否可用于复制实际上与这个问题无关:隐式声明(删除)的复制构造函数比模板构造函数更匹配。
  • 那(注释)暗示编译器永远不会实例化模板来生成复制构造函数,不是吗?也就是说,隐式声明总是比任何模板化构造函数更好的匹配,这实际上意味着虽然标准似乎没有明确禁止将该模板用作 copy-constructor,但事实是它永远不会。还是会?
  • gcc/libstdc++ 的成员添加了以下内容: shared_ptr(const shared_ptr&) noexcept = default;到 std::shared_ptr
  • @DougGregor 谢谢,谢谢!您从“svn.boost.org”传递的链接帮助我解决了 Caffe 和 Ubuntu 12.04 的问题。非常感谢!
【解决方案2】:

在这种情况下,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 使用它自己的标准库来编译上面的代码。似乎它正在选择 templated 构造函数作为复制构造的可行重载,这让我回顾标准以验证这是否是一个错误——我一直认为模板不能被用作拷贝构造函数——或者没有。

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

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

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

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2015-10-15
    • 2013-01-16
    • 2015-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-20
    • 2014-05-23
    • 2018-04-27
    相关资源
    最近更新 更多