【问题标题】:Broken std::unique_ptr in GCC 4.7.2GCC 4.7.2 中的 std::unique_ptr 损坏
【发布时间】:2012-12-28 02:08:34
【问题描述】:

我正在尝试使用std::unique_ptr 来存储一些不透明对象的整数句柄。为此,我定义了一个自定义删除器类型,它执行typedef int pointer,以便将原始指针类型覆盖为int 而不是int*。这个过程在本站最后一段描述:http://asawicki.info/news_1494_unique_ptr_in_visual_c_2010.html

这里有一些示例代码可以更好地说明我正在尝试做的事情:

#include <memory>
#include <iostream>

static void close(int p)
{
    std::cout << p << " has been deleted!" << std::endl;
}

struct handle_deleter
{
    typedef int pointer;
    void operator()(pointer p) { close(p); }
};

typedef std::unique_ptr< int, handle_deleter> unique_handle;

int main(int argc, char *argv[])
{
    unique_handle handle(1);

    return 0;
}

当我使用 GCC 4.7.2 编译此代码时,我收到以下错误:

In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/memory:86:0,
                 from unique_ptr_test.cpp:1:
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/unique_ptr.h: In instantiation of ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = int; _Dp = handle_deleter]’:
unique_ptr_test.cpp:19:23:   required from here
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/unique_ptr.h:172:2: error: invalid operands of types ‘int’ and ‘std::nullptr_t’ to binary ‘operator!=’

~unique_ptr 过程的代码如下:

// Destructor.
~unique_ptr() noexcept
{
    auto& __ptr = std::get<0>(_M_t);
    if (__ptr != nullptr)
      get_deleter()(__ptr);
    __ptr = pointer();
}

据我说,对 nullptr 的检查没有意义,因为原始指针类型是 int(而不是 int*,因为在 HandleDeleter 中覆盖它)。奇怪的是,这段代码在 GCC 4.6.1 下编译没有错误。执行后,示例显示“1 已被删除!”正如预期的那样。

我想知道是否有任何我忽略的细节,或者它是否真的是 GCC 的 unique_ptr 的 STL 实现中的错误。

谢谢,

PMJ

【问题讨论】:

  • 专业提示:首先搜索您的代码/理解中的错误,不要立即责怪实现“损坏”。现在,Deleter::pointer 必须满足 Nullable Pointer 要求。这包括与nullptr 的比较,因为std::unique_ptr 被指定为仅在get() != nullptr 时调用删除器。 IOW,你只需要包装你的 int 并将其用作 pointer 类型。
  • std::unique_ptr&lt;T&gt; 表示指向T​​的指针。没有办法让std::unique_ptr&lt;T&gt;::pointer 成为int。换句话说,您忘记了应用于第一个模板参数的隐式星号。
  • @Xeo:为什么这不是一个答案! :-)
  • @Xeo:感谢您的提示,尽管老实说,我通常会按照您所说的进行操作(先怪我自己的代码)。我的真正意思是“损坏”,因为在 unique_ptr 实现中的一些行为变化,过去使用 GCC 的早期版本正确编译的东西现在无法编译。这适用于我提供的链接中的代码以及我在许多网站上看到的 unique_ptr 的类似代码示例。但是,我承认用词不当。
  • @GManNickG:这纯粹是一个语义问题。句柄基本上是一种特殊的不透明指针。此外,如果一个人不能使用除了 T* 之外的任何东西作为原始指针类型,那么为什么明确允许使用自定义删除器覆盖 std::unique_ptr&lt;T&gt;::pointer typedef?

标签: c++ c++11 unique-ptr


【解决方案1】:

正如我在评论中所说,如果您传递的删除器类型具有嵌套的pointer typedef,则它必须满足 NullablePointer 要求:

20.7.1.2 [unique.ptr.single] p3

如果remove_reference&lt;D&gt;::type::pointer 类型存在,那么unique_ptr&lt;T, D&gt;::pointer 应该是remove_reference&lt;D&gt;::type::pointer 的同义词。否则unique_ptr&lt;T, D&gt;::pointer 应为T* 的同义词。 unique_ptr&lt;T, D&gt;::pointer 类型应满足NullablePointer (17.6.3.3) 的要求

然后§17.6.3.3 列出了类型必须满足的所有要求才能成为 NullablePointer。某些语义列在一个表中,其中:

u 表示标识符,t 表示P 类型的非常量左值,ab 表示类型的值(可能是constPnp表示类型的值(可能是conststd::nullptr_t

Expression    Return type                         Operational semantics
P u(np);                                          post: u == nullptr
P u = np;
P(np)                                             post: P(np) == nullptr
t = np        P&                                  post: t == nullptr
a != b        contextually convertible to bool    !(a == b)
a == np       contextually convertible to bool    a == P()
np == a
a != np       contextually convertible to bool    !(a == np)
np != a

现在,最简单的解决方案是将您的 int 包装在提供这些语义的类型中:

#include <cstddef> // std::nullptr_t

struct handle{
  handle() : value(0){}
  handle(std::nullptr_t) : value(0){}
  /*explicit*/ handle(int v) : value(v){} // make it explicit if you need it
  // special members can be generated

  handle& operator=(std::nullptr_t){ value = 0; return *this; }

  // contextual conversion to bool
  explicit operator bool() const{ return value != 0; }

  int value;
};

bool operator==(handle lhs, handle rhs){ return lhs.value == rhs.value; }
bool operator!=(handle lhs, handle rhs){ return lhs.value != rhs.value; }
// comparision against 'nullptr' is handled by the above operators
// since 'nullptr' can be implicitly converted to 'handle'

【讨论】:

  • 请注意,如果0 对您的句柄来说是一个有意义的值,那么您可能希望专门处理与nullptr 的比较。或者,如果您有任何无意义的值,请将 value 设置为 nullptr 构造函数中的值。
  • 感谢您的深入解释。现在,我可以清楚地看到为什么这种方法是错误的。我想我可以通过使用(强制)std::unique_ptr 来存储整数句柄来节省一些工作。毕竟,也许从头开始实现 unique_handle 会更容易。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-11
  • 1970-01-01
相关资源
最近更新 更多