在std::allocator重新绑定时转移对象所有权

3

我有一个Visual Studio 2008 C++应用程序,我正在实现替换容器中使用的标准分配器(如std::vector)的功能。但是,我遇到了一个问题。我的实现依赖于分配器拥有资源句柄的所有权。在使用rebind特性的情况下,我需要将句柄的所有权转移给新的分配器。类似这样:

template< class T >
class MyAllocator
{
public:
    template< class U >
    explicit MyAllocator( const MyAllocator< U >& other ) throw() 
        :  h_( other.Detach() ) // can't do this to a `const`
    {
    };

    // ...

private:
    HANDLE Detach()
    {
        HANDLE h = h_;
        h_ = NULL;
        return h;
    };

    HANDLE h_;
}; // class MyAllocator

很遗憾,我无法释放旧分配器的句柄所有权,因为它是const。如果我从重新绑定构造函数中删除const,那么容器将不会接受它。

error C2558: class 'MyAllocator<T>' : no copy constructor available or copy constructor is declared 'explicit'

有没有好的方法解决这个问题?

分配器不需要有状态吗?? 我记得Matt Austern在某个地方写了一篇经典的文章,但我现在找不到它…… - sbi
2
这是文章链接:http://drdobbs.com/cpp/184403759。从快速浏览此讨论来看,似乎支持有状态分配器,尽管以前支持的 std lib 实现曾经是一个问题。 - sbi
@sbi - 感谢您的文章和讨论。我想问一个关于如何处理std::swap的后续问题。 - PaulH
4个回答

2

虽然我对分配器不太了解(从未需要过),但你的复制构造函数采用const引用,因此承诺不更改other对象,但是你仍然试图更改它。尽管有些类是设计成这样的(std::auto_ptr),但这似乎有问题。

从句法上讲,你可以始终声明h_mutable,并将Detach()作为一个const成员函数,但在使用大刀砍伐语法丛林之前,我会严肃质疑这种设置的语义。


1

如果你将h_声明为mutable,会发生什么?


1

你可以通过增加一个额外的间接层来解决这个问题,但这并不是一个理想的解决方案。基本上,你的分配器会有一个指向句柄的指针,该句柄将在构造函数/析构函数中进行分配/释放。它所指向的句柄将始终是非const的,因此你可以将句柄从一个分配器“移动”到另一个分配器。但这确实会给分配器增加一些开销。

我不知道你的具体情况,但似乎应该仔细考虑非平凡可复制的有状态分配器的所有影响。是否有其他方法可以简化其设计,使其不具有仅限移动的句柄?


1

您无法转移所有权,因为即使在单个容器中,分配器可能会被复制和重新绑定多次,并且生成的实例同时使用。

您需要共享该资源。可以创建一个带有引用计数的资源间接方式。例如:

class SharedHandle {
    HANDLE h_;
    int count;
    SharedHandle(HANDLE h) : h_(h), count(1) {}
    ~SharedHandle() { CloseHandle(h_); } // or whatever to release the resource.
    SharedHandle *Ref() { ++count; return this; }
    void Unref() { if(!--count) delete this; }
}

然后:

explicit MyAllocator( const MyAllocator< U >& other ) throw() 
:  h_( other.h_->Ref() )

除了像hash_map/unordered_map这样自然需要分配异构块的容器之外,微软的容器实现还会分配各种奇怪的东西。当我跟踪一个Windows应用程序中的分配时,发现有许多来自STL内部某处的奇怪大小的分配。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接