12

から派生したカスタム STL アロケータを作成しようとしていますstd::allocatorが、どういうわけかすべての呼び出しがallocate()基本クラスに移動します。このコードに絞り込みました:

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return 0;
    }
};

int main()
{
    vector<int, a<int>> v(1000, 42);
    return 0;
}

「よう!」と期待しています。実際には何も割り当てていないため、印刷された後、恐ろしいエラーが発生しました。代わりに、プログラムは正常に実行され、何も出力されません。私は何を間違っていますか?

gcc と VS2008 で同じ結果が得られます。

4

4 に答える 4

7

再バインドメンバーテンプレートと、C++標準のアロケータ要件にリストされているその他のものを提供する必要があります。たとえば、だけでなくallocator<T>、も受け入れるテンプレートコピーコンストラクタが必要allocator<U>です。たとえば、あるコードで実行できる場合があります。たとえば、std::listで実行される可能性があります。

template<typename Allocator>
void alloc1chunk(Allocator const& alloc) {
    typename Allocator::template rebind<
        wrapper<typename Allocator::value_type>
      >::other ot(alloc);
    // ...
}

正しい再バインドテンプレートが存在しないか、対応するコピーコンストラクターが存在しない場合、コードは失敗します。要件が何であるかを推測することはどこにも役立ちません。遅かれ早かれ、これらのアロケータ要件の一部に依存するコードを使用する必要があり、アロケータがそれらに違反しているため、コードは失敗します。の標準のコピーを作成するドラフトでそれらを確認することをお勧めします20.1.5

于 2009-02-12T03:01:20.213 に答える
5

この場合の問題は、アロケーターの rebind メンバーをオーバーライドしなかったことです。このバージョンは動作します (VS2008):

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return 0;
    }

    template <typename U> struct rebind
    {
        typedef a<U> other;
    };
};

int main() {
    vector<int, a<int>> v(1000, 42);
    return 0;
}

STLヘッダーを介してデバッグすることでこれを見つけました。

ただし、これが機能するかどうかは、STL の実装に完全に依存するため、最終的には、このようにすべきではないという点で Klaim は正しいと思います。

于 2009-02-12T02:12:29.283 に答える
2

カスタマイズされたアロケーターを作成するためのテンプレートが 2 つあります。カスタムタイプで使用される場合、最初のものは自動的に機能します。

template<>
class std::allocator<MY_TYPE>
{
public:
    typedef size_t      size_type;
    typedef ptrdiff_t   difference_type;
    typedef MY_TYPE*    pointer;
    typedef const MY_TYPE*  const_pointer;
    typedef MY_TYPE&    reference;
    typedef const MY_TYPE&  const_reference;
    typedef MY_TYPE     value_type;

    template <class U>
    struct rebind
    {
        typedef std::allocator<U> other;
    };

    pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
    {
        return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
    }
    void construct(pointer p, const_reference val)
    {
        ::new(p) T(val);
    }
    void destroy(pointer p)
    {
        p->~T();
    }
    void deallocate(pointer p, size_type n)
    {
        FREE_FUNC(p);
    }
    size_type max_size() const throw()
    {
        // return ~size_type(0); -- Error, fixed according to Constantin's comment
        return std::numeric_limits<size_t>::max()/sizeof(MY_TYPE);
    }
};

2 番目は、char、wchar_t、std::string などの標準アロケーターを使用して、事前定義された型に独自のアロケーターが必要な場合に使用されます。

    namespace MY_NAMESPACE
    {

    template <class T> class allocator;

    // specialize for void:
    template <>
    class allocator<void>
    {
    public:
        typedef void*       pointer;
        typedef const void* const_pointer;
        // reference to void members are impossible.
        typedef void        value_type;

        template <class U>
        struct rebind
        {
            typedef allocator<U> other;
        };
    };

    template <class T>
    class allocator
    {
    public:
        typedef size_t      size_type;
        typedef ptrdiff_t   difference_type;
        typedef T*      pointer;
        typedef const T*    const_pointer;
        typedef T&      reference;
        typedef const T&    const_reference;
        typedef T       value_type;

        template <class U>
        struct rebind
        {
            typedef allocator<U> other;
        };

        allocator() throw()
        {
        }
        template <class U>
        allocator(const allocator<U>& u) throw()
        {
        }
        ~allocator() throw()
        {
        }

        pointer address(reference r) const
        {
            return &r;
        }
        const_pointer address(const_reference r) const
        {
            return &r;
        }
        size_type max_size() const throw()
        {
            // return ~size_type(0); -- Error, fixed according to Constantin's comment
            return std::numeric_limits<size_t>::max()/sizeof(T);
        }
        pointer allocate(size_type n, allocator<void>::const_pointer hint = 0)
        {
            return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
        }
        void deallocate(pointer p, size_type n)
        {
            FREE_FUNC(p);
        }

        void construct(pointer p, const_reference val)
        {
            ::new(p) T(val);
        }
        void destroy(pointer p)
        {
            p->~T();
        }
    };

template <class T1, class T2>
inline
bool operator==(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
    return true;
}

template <class T1, class T2>
inline
bool operator!=(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
    return false;
}

}

上記の最初のテンプレートは、独自に定義された型用であり、それ以上の処理は必要ありませんが、標準のコンテナー クラスによって自動的に使用されます。2 番目のテンプレートを標準タイプで使用する場合は、さらに作業が必要です。たとえば、std::string の場合、その型の変数を宣言するときは、次の構成を使用する必要があります (typedef を使用すると最も簡単です)。

std::basic_string<char>, std::char_traits<char>, MY_NAMESPACE::allocator<char> >
于 2009-02-12T17:07:45.437 に答える
1

次のコードは、期待どおりに「yo」を出力します。これは、古くからの友人である「未定義の動作」でした。

#include <iostream>
#include <vector>
using namespace std;

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return new T[10000];
    }
};

int main()
{
    vector<int, a<int> > v(1000, 42);
    return 0;
}

編集: デフォルトのアロケータに関する C++ 標準をチェックアウトしました。それから継承することは禁止されていません。実際、私の知る限り、標準のどの部分にもそのような禁止事項はありません。

于 2009-02-12T01:35:37.017 に答える