Boost.Variantは再帰可能なので最強。
この場合、sizeof(v)はどうなるのか。実装依存だけど、std::mapのサイズはsizeof(v)に影響しないことは言える。ストレージ上には次の疑似コードのようにポインタとして保持される。
recursive_variantで書き直すと、次のようになる。
boost::recursive_warpper<T>を使うと、もれなくnew Tが付いてくる。どうしてだろう。Boost.Functionみたいにsmall object判定してくれたっていいじゃないか。もちろん、
のようなコードはサイズが定まらないから無理だけれど、
みたいな場合、つまり、
場合はsmall objectにできるかもしれない。っていうか、make_recursive_variantは魔法でそういうことをやってくれてると勝手に妄想してた。なんでだろう。
まあ、このあたりはMLでも後で眺めてみるとして、この挙動をどうにかしたければ、たぶん、boost/include/variant/detail/enable_recursive.hppをいじれば良いらしい。これも後でやってみる。
こっから先はrecursive_variant_を解決する部分を読んだときのメモ。
プリプロセス済みのenable_recursive
substituteメタ関数の結果がTかリファレンスかポインタでなければ、recursive_wrapper登場。
そしてnew Tが発生。
つまり、std::map<std::string, boost::recursive_variant_>の場合、
になる。
【2010-02-27追記】アロケータのことを忘れてた。まあ、話の筋は変わらないけれど。
typedef boost::make_recursive_variant< boost::blank, std::string, std::map<std::string, boost::recursive_variant_> >::type variant0;
この場合、sizeof(v)はどうなるのか。実装依存だけど、std::mapのサイズはsizeof(v)に影響しないことは言える。ストレージ上には次の疑似コードのようにポインタとして保持される。
// 疑似コード union u { boost::blank u0; std::string u1; std::map<std::string, u>* u2; };
recursive_variantで書き直すと、次のようになる。
struct map; typedef boost::make_recursive_variant< boost::blank, std::string, boost::recursive_wrapper<map> >::type variant1; struct map : std::map<std::string, variant> {};
boost::recursive_warpper<T>を使うと、もれなくnew Tが付いてくる。どうしてだろう。Boost.Functionみたいにsmall object判定してくれたっていいじゃないか。もちろん、
typedef boost::make_recursive_variant< boost::blank, std::string, std::pair<std::string, boost::recusive_variant_> >::type variant2;
のようなコードはサイズが定まらないから無理だけれど、
struct large_object { char data[256]; }; typedef boost::make_recursive_variant< boost::blank, std::string, std::map<std::string, boost::recursive_variant_>, large_object >::type variant3;
みたいな場合、つまり、
- Variant内に充分大きなストレージが確保されている。
- 再帰する型のサイズがVariantのサイズから独立である。
場合はsmall objectにできるかもしれない。っていうか、make_recursive_variantは魔法でそういうことをやってくれてると勝手に妄想してた。なんでだろう。
- 面倒だから?
- 必要ないから?
- 標準に合致しなくなる?
- 実装上問題がある?
まあ、このあたりはMLでも後で眺めてみるとして、この挙動をどうにかしたければ、たぶん、boost/include/variant/detail/enable_recursive.hppをいじれば良いらしい。これも後でやってみる。
こっから先はrecursive_variant_を解決する部分を読んだときのメモ。
- boost/variant/variant.hpp
- boost::variant<typename... T>::recursive_enabled_types
- 再帰している場合の型リスト
- quoted_enable_recursiveでrecursive_variant_を外す
- boost/variant/detail/enable_recursive(_fwd).hpp
- quoted_enable_recursive<typename RecursiveVariant, typename NoWrapper = mpl::false_>
- 単にenable_recursiveをquoteする
- enable_recursive<T, typename RecursiveVariant, typename NoWrapper>
- 型リストの各要素T_iについて、
- enable_recursive<T_i, wknd_self_t, mpl::false>
- wknd_self_tはvariant<T...>
プリプロセス済みのenable_recursive
template <typename T, typename RecursiveVariant> struct enable_recursive<T, RecursiveVariant, mpl::false_> { private: typedef typename substitute<T, RecursiveVariant, ::boost::recursive_variant_>::type t_; public: typedef typename mpl::if_< mpl::or_<is_same<t_, T>, is_reference<t_>, is_pointer<t_> >, t_, boost::recursive_wrapper<t_> >::type type; };
substituteメタ関数の結果がTかリファレンスかポインタでなければ、recursive_wrapper登場。
そしてnew Tが発生。
template <typename T, typename Dest, typename Source, typename Arity> struct substitute { typedef T type; }; template <typename Dest, typename Source> struct substitute<Source , Dest , Source , mpl::int_<-1> > { typedef Dest type; }; template < template < typename P1 , typename P2 > class T, typename U1, typename U2, typename Dest, typename Source > struct substitute<T<U1, U2>, Dest, Source, mpl::int_<(2)> > { private: typedef typename substitute<U1, Dest, Source>::type u1; typedef typename substitute<U2, Dest, Source>::type u2; public: typedef T<u1, u2> type; };
つまり、std::map<std::string, boost::recursive_variant_>の場合、
u1 = std::string u2 = RecursiveVariant type = std::map<std::string, RecursiveVariant>
になる。
【2010-02-27追記】アロケータのことを忘れてた。まあ、話の筋は変わらないけれど。
このページへのコメント
8yXWl8 <a href="http://ytoaqnjmsjbl.com/">ytoaqnjmsjbl</a>, [url=http://rmqazbpnhkxt.com/]rmqazbpnhkxt[/url], [link=http://lcstmovkbqbn.com/]lcstmovkbqbn[/link], http://wbqqpgghcigh.com/