C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[11]: テンプレートの特殊化について


(過去ログ 84 を表示中)

[トピック内 12 記事 (1 - 12 表示)]  << 0 >>

■49773 / inTopicNo.1)  テンプレートの特殊化について
  
□投稿者/ 雲 (1回)-(2010/05/16(Sun) 16:15:49)

分類:[C/C++] 

最近、興味があってSTLのallocatorを作成しようとしています(正確に言うと、カスタム allocator を作ってみようとしています)。プログラミング言語C++第3版のp.650あたりを参考に作成してみたのですが、一つ、わからないことがあります。

allocator は(質問に関係するところだけ書くと)

template<typename T>
class allocator{
public:
  (略)
T* allocate( size_t n, allocator<void>::const_pointer hint = 0 ); ← 2番目の引数の宣言でエラーが出ます。ちなみに、void* に置き換えると OK
  (略)
};

と書かれています。また、allocate の引数を見ると、allocator を void で特殊化してあります。なので、allocator を void で特殊化したクラスを上記のクラス宣言の後に書いてみました。

template<>
class allocator<void>{
(略)
};

ここで、元々のallocator のクラスで宣言されているallocateの関数定義をクラスの外で書こう思って、

template<typename T>
T* HeapAllocator<T>::allocate( size_t num, HeapAllocator<void>::const_pointer ptr )
{
return( 0 ); ←(テストなので、0 を戻す)
}

と書いてみましたが、visualStudio 2010 でコンパイルエラーとなりました。いろいろと試してみましたが、どうやら、allocator の関数宣言のところで、
allocator<void> と allocator の特殊化したものを使おうとしているのが気に入らないようです。

コンパイルエラーを避けるだけなら、単純に、allocator<void>::pointer を void* に置き換えると良いのですが、なんだかすっきりしません。

何か、書き方が誤っているだけだと思うのですが残念ながら調べてみてもどう書くべきかさっぱりです。

こういう場合、どのように記述すると良いのかについてヒントだけでもご教授頂けると幸いです。

よろしくお願いします。




引用返信 編集キー/
■49783 / inTopicNo.2)  Re[1]: テンプレートの特殊化について
□投稿者/ やんち (8回)-(2010/05/17(Mon) 02:37:51)
やんち さんの Web サイト
たぶん、「void *」は型名だけど、「void」は型名じゃないから、と言う事だと思います。

引用返信 編集キー/
■49784 / inTopicNo.3)  Re[2]: テンプレートの特殊化について
□投稿者/ 雲 (2回)-(2010/05/17(Mon) 04:45:50)
No49783 (やんち さん) に返信
> たぶん、「void *」は型名だけど、「void」は型名じゃないから、と言う事だと思います。

void abc; は、NG だけど、void *abc; はOKということでしょうか?
このことと、今回の質問内容との関連については、やんちさんの回答の意味が理解できませんでした。


visualStudio2010 でのエラーを書き忘れていましたので、追記します。

error C2244: 'HeapAllocator<T>::allocate' : 関数の定義を既存の宣言と合致させることができませんでした。
定義
'HeapAllocator<T>::pointer HeapAllocator<T>::allocate(HeapAllocator<T>::HeapAllocator<T>::size_type,void *)'
既存の宣言
'T *HeapAllocator<T>::allocate(HeapAllocator<T>::size_type,const T *)'

とエラーを言われてしまいます。なので、宣言と定義で型が違うのが原因で、(何故か)宣言の所は T* に置き換えられている。ということが課題のようです。

引用返信 編集キー/
■49786 / inTopicNo.4)  Re[3]: テンプレートの特殊化について
□投稿者/ 774RR (502回)-(2010/05/17(Mon) 10:05:23)
2010/05/17(Mon) 17:02:37 編集(投稿者)
const reference が nonconst だったのを編集

自作 allocator を作っても list に対して実用できないわけだし、
正直なぜこんな難度の割りに益が少ないところをコネコネしようとしているのかよくわからないが・・・
# もっと簡単かつ利の多いところがありそうな気がする

とりあえずウチの gcc-4.3.3 & VS2005 の両方でコンパイルできるソースを提示しておく。解説略。
解説するとしたら厳密な話がお望み?それとも「こんなもん」というレベルの話をお望み?

#include <iostream>
#include <vector>
#include <cstdlib>

#define ALLOCATE_OUTSIDE_CLASS 0

template<typename T>struct myallocator;
template<> struct myallocator<void> {
    typedef void* pointer;
    typedef const void* const_pointer;
    typedef void value_type;
    template<typename U> struct rebind {typedef myallocator<U> other; };
};
template<typename T> struct myallocator {
    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<typename U> struct rebind { typedef myallocator<U> other; };
    myallocator() throw() {}
    myallocator(const myallocator&) throw() {}
    template<typename U> myallocator(const myallocator<U>&) throw() {}
    size_type max_size() const throw() { return -1; }
    void destroy(T* p) { p->~T(); }
    pointer address(reference x) const { return &x; }
#if ALLOCATE_OUTSIDE_CLASS
    pointer allocate(size_type n, myallocator<void>::const_pointer hint=0);
#else
    pointer allocate(size_type n, myallocator<void>::const_pointer hint=0) {
        std::cout << "myallocator::allocate(" << n << "," << static_cast<const void*>(hint) << ")";
        void* p=malloc(n*sizeof(T));
        std::cout << " returning p=" << p << std::endl;
        return reinterpret_cast<pointer>(p);
    }
#endif
    void deallocate(pointer p, size_type n) {
        std::cout << "myallocator::deallocate(" << static_cast<void*>(p) << "," << n << ")\n";
        free(p);
    }
    void construct(pointer p, const T& value) {
        std::cout << "myallocator::construct(" << static_cast<void*>(p) << "," << value << ")\n";
        *p=value;
    }
};
#if ALLOCATE_OUTSIDE_CLASS
template<typename T>
T* myallocator<T>::allocate(typename myallocator<T>::size_type n, typename myallocator<void>::const_pointer hint) {
    std::cout << "myallocator::allocate(" << n << "," << static_cast<const void*>(hint) << ")";
    void* p=malloc(n*sizeof(T));
    std::cout << " returning p=" << p << std::endl;
    return reinterpret_cast<typename myallocator<T>::pointer>(p);
}
#endif

int main() {
    std::vector<int, myallocator<int> > l;
    l.reserve(100);
    l.push_back(999);
    l.push_back(998);
}

引用返信 編集キー/
■49794 / inTopicNo.5)  Re[4]: テンプレートの特殊化について
□投稿者/ 雲 (3回)-(2010/05/17(Mon) 22:52:57)
No49786 (774RR さん) に返信
> 正直なぜこんな難度の割りに益が少ないところをコネコネしようとしているのかよくわからないが・・・
> # もっと簡単かつ利の多いところがありそうな気がする

確かに、普通に考えるとそうだと思います(きっと、損な性分です)。

何故、自作 allocator に走っているのかというと、社内ツールを作成したときにメモリが解放されなくて困った経験があるからというのが
一つの動機です(No42238)。もう一つは、単に、興味があるということですね。

まぁ、そうは言っても、vector でメモリが解放されないのならVirtualAllocなんかを適切に使えば良いのですが、ちょっと、正直、悔しかった
というのがあって、今回、自作allocatorでリベンジを試みているわけです。
(ご指摘の通り、労多く実少なしですね)

> とりあえずウチの gcc-4.3.3 & VS2005 の両方でコンパイルできるソースを提示しておく。解説略。

ありがとうございます。

> 解説するとしたら厳密な話がお望み?それとも「こんなもん」というレベルの話をお望み?

私が理解したところでは、クラスの先行宣言を行って特殊化のクラス宣言を先に行うと言うことで allocator<void>::pointer を特殊化したもので
置き換えるということです。

なるほど。。。週末、ひたすら考えてもわからなかったことを考えると、ちょっと、嬉しい。これで、HeapAllocを指定してメモリの確保を
やって試してみたいです(カスタムでHeapを作成すれば、きっと、メモリも全て解放されるはず)。

結果は、また、報告をしたいと思いますので、その際にはよろしくお願いします。

ありがとうございました。


解決済み
引用返信 編集キー/
■49795 / inTopicNo.6)  Re[5]: テンプレートの特殊化について
□投稿者/ 雲 (4回)-(2010/05/17(Mon) 22:54:49)
ごめんなさい。No42238へのリンクが切れていましたので、以下に、直リンクを書かせてください。

http://bbs.wankuma.com/index.cgi?mode=al2&namber=42238&KLOG=72
解決済み
引用返信 編集キー/
■49800 / inTopicNo.7)  Re[6]: テンプレートの特殊化について
□投稿者/ 774RR (503回)-(2010/05/18(Tue) 09:50:26)
まあ解決済みなのだがせっかく書いたし・・・

JIS X 3014 や ISO/IEC 14882 (C++ 言語仕様書)において
20.4.1 の節表題には The default allocator としか書いてない。
この文言の解釈は
「ユーザーが特に指定を行わなかったらこの形式の allocator が使われる」なのか
「ユーザーが自作する allocator はこの形式で無ければならない」なのか、
規格書に解説が無いので解釈が微妙なところ。
俺は前者と読んだ、つまり規格書が暗に主張している内容は
「ユーザーが自作する allocator は必ずしもこの形式にのっとる必要はない」だ。

[システムが用意している allocator ] と [末端プログラマが書くべき allocator ] が同一である必然が無いなら
[システムが用意している allocator ] に厳密一致させるために allocator<void>::const_pointer と書かなくてよく
それならば、そもそも allocator<void> という特殊化をする必要すらないのであって、
であれば最初から
template<typename T> struct myallocator { ...
 pointer allocate(size_type n, const void* = 0) { ... }
}; // で事足りる (特殊化の必要がない、だから先行宣言も必要ない)
# 実際この記述でコンパイルエラーにならないし正常に期待通りに動く。

俺が入手して使っている C++ 処理系すべてで、コンテナがメモリを要求する際に hint 引数を渡している
ものって存在しないんだ(GNU CC-3.x, GNU CC-4.x, VS2005)
メモリを要求する際に非0な hint を必ず指定するような俺俺コンテナを自作したとしても、
アロケータ側は hint を使ってもいいが無視してもよいわけで、
規格書の文言としては hint の指定を許しているが、現実には死案件と言い切っていいんぢゃないかな。

深く気にしないほうがよさそう。

解決済み
引用返信 編集キー/
■49824 / inTopicNo.8)  Re[7]: テンプレートの特殊化について
□投稿者/ 雲 (5回)-(2010/05/18(Tue) 22:11:39)
No49800 (774RR さん) に返信
> それならば、そもそも allocator<void> という特殊化をする必要すらないのであって、
> であれば最初から
> template<typename T> struct myallocator { ...
>  pointer allocate(size_type n, const void* = 0) { ... }
> }; // で事足りる (特殊化の必要がない、だから先行宣言も必要ない)
> # 実際この記述でコンパイルエラーにならないし正常に期待通りに動く。

全くその通りで、規格書までは読みこなす力は不足していますが、えぴさんのStandard Template Libraryプログラミング p.39 以降に
記載されている allocator は、void *hint と記述してあります。

今回の質問は、void * と書けば特に問題ないと言うことは何となく理解した上で、そうは言っても、テンプレートの特殊化したものに
含まれる型を参照したい時にどうするの?というこがわからなくて質問しました。
(質問の書き方がそうは取れないようでしたら、ごめんなさい)

なので、774RR さんの回答はとってもわかりやすくて、とてもありがたかったです。

ただ、一つ、

> 自作 allocator を作っても list に対して実用できないわけだし

こう言われる理由が思いつかなくて困っています。

たぶん、list<int> などと記述した際に、int の型だけでなくリストのポインタ型をメモリ確保する必要があるのに、
allocator<int> を指定してしまうと、int 型以外の型をメモリ確保する時に使えない。ということを
言っているのだと思いますが、そうであるならば、rebind を使えば良いように感じました。

もしかすると、大きな誤解をしているような気もしますのでご指摘いただけると幸いです。


もし、何か、誤解しているところがあるようでしたら



引用返信 編集キー/
■49830 / inTopicNo.9)  Re[8]: テンプレートの特殊化について
□投稿者/ 774RR (507回)-(2010/05/19(Wed) 09:36:20)
ああごめん。大昔の VC++6 とか gcc-2.95 とかその辺の記憶が頭に残ってたので間違ってた。
現代処理系ではなんも問題ないっす。

俺の知識整理+第三者読者のために無駄なまとめをしておくことにする。

list の典型的内部構造は(すべての処理系でこうであると断言する気は無いが)
template<typename T> struct internal_node {
 internal_node* prev;
 internal_node* next;
 T element;
};
のようになっている。
そのため list<int> の中の人は internal_node<int> を allocate したいと思っている。
一方で list<int> の allocator を自作したい人は myallocator<int> を渡したいと思っている。
list<int, myallocator<int> > mylist; // みたいに。
list に限り list<int, myallocator<list<int>::internal_node> > と書く必要があるとしたら
中の人は実装を晒す必要があって困ってしまうし、
外の人は使い分ける必要があって困ってしまうし、
両者にとっていいことが何一つ無い。

そのための機構が rebind であり list の中の人は次のように使っている。
typedef typename _Alloc::template rebind<internal_node<value_type> >::other node_allocator;
古いコンパイラはこの記法が通らないものが多くてどうにもならなかったけど
現代コンパイラであれば問題ない。
そして myallocator はこの node_allocator を渡して呼ばれるようになっている。

問題ないっちゃ問題ないんだけど、次の点には注意しておく必要がある、ってことね。
外の人は myallocator<int> を渡しているので、実際使われるのも myallocator<int> だと思っていたら
いつのまにか myallocator<std::list_internal_node<int> > になっていた。
ポルナレフ AA 略
引用返信 編集キー/
■49831 / inTopicNo.10)  Re[9]: テンプレートの特殊化について
□投稿者/ 774RR (508回)-(2010/05/19(Wed) 12:10:18)
あ、あとついでに先のサンプルを実用しようとしている人に注意。

construct でコンストラクトしてない(代入してる)のでそこは要修正。
placement ::new を使う必要がある
void construct(pointer p, const T& val) { ::new(static_cast<void*>(p)) T(val); }

<cstdlib> を使っているので std::malloc std::free に修正

max_size() が単なる手抜きなのでもっとましなものに修正

ってかこのくらいなら GCC-4 の ext/malloc_allocator.h を見ればいいし
もっとより凝った例が mt_allocator.h bitmap_allocator.h pool_allocator.h など用意されてる。
引用返信 編集キー/
■49838 / inTopicNo.11)  Re[10]: テンプレートの特殊化について
□投稿者/ 雲 (6回)-(2010/05/19(Wed) 18:26:47)
No49831 (774RR さん) に返信
> ってかこのくらいなら GCC-4 の ext/malloc_allocator.h を見ればいいし
> もっとより凝った例が mt_allocator.h bitmap_allocator.h pool_allocator.h など用意されてる。

見てみましたが、挫折してしまいました。。。また、もう少し、力を付けて再チャレンジをしたいと思います。

なかなか、自分の実力のなさを実感してしまいます。

頑張らねば。

ありがとうございました。

解決済み
引用返信 編集キー/
■49843 / inTopicNo.12)  Re[11]: テンプレートの特殊化について
□投稿者/ 774RR (509回)-(2010/05/20(Thu) 10:41:19)
malloc_allocator.h はそんなに難しくないと思うが・・・俺の例とほとんど同じだし。
# 俺のサンプル malloc が失敗した場合に throw(bad_alloc) してないや

pool_allocator.h などは malloc の中でやってる詳細実装をこの中にもってきているので
(alignment の確保とかロックとか)ある意味、難しくて当たり前。

・・・で、この辺 allocator<void> の特殊化のようなこと一切入っていないの気づいてる?
allocator 自体はそういうこと知らなくていい、ってこったね。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -