Template Aliases でハマった

Template Aliases でハマったので覚え書き。
まーしょっぱいエラーですね。
次のコードはエラーになります。

[ソース]

#include <boost/mpl/has_xxx.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/if.hpp>

namespace mpl = boost::mpl;

BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);

template<typename T>
using T_value_type = mpl::identity<typename T::value_type>;

BOOST_MPL_HAS_XXX_TRAIT_DEF(type)

template<typename T>
using T_type = mpl::identity<typename T::type>;

template<typename T>
using select = typename mpl::if_<
    has_value_type<T>,
    T_value_type<T>,
    typename mpl::if_<
        has_type<T>,
        T_type<T>,
        mpl::identity<T>
    >::type
>::type;

struct vec{
    typedef int value_type;
};

// 以下 ::type や ::value_type が参照できなくてエラー
using type1 = select<mpl::identity<float> >::type;
using type2 = select<int>::type;
using type3 = select<vec>::type;


つまりはこういう事らしい。

[ソース]

template<typename T>
struct TT_type : T::type{};

template<typename T>
using T_type = typename T::type;

struct foo{};

typedef TT_type<foo> hoge;     // ok
typedef T_type<foo>  hoge2;    // error
TT_type<foo> h;                // error

まぁ厳密に仕様を読み込んでいるわけではないので、よく分かっていませんが……。
上記のソースだと簡略化しているので、すぐにエラー原因が分かるんですけど、元のソースでかなりアレな使い方をしていたので原因が分かるまで結構苦労しました……。


解決策としては、以下の様に struct を使用すれば動くようになります

#include <boost/mpl/has_xxx.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/if.hpp>

namespace mpl = boost::mpl;

BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);

// struct を使う
template<typename T>
struct T_value_type : mpl::identity<typename T::value_type>{};

BOOST_MPL_HAS_XXX_TRAIT_DEF(type)

template<typename T>
struct T_type : mpl::identity<typename T::type>{};

template<typename T>
using select = typename mpl::if_<
    has_value_type<T>,
    T_value_type<T>,
    typename mpl::if_<
        has_type<T>,
        T_type<T>,
        mpl::identity<T>
    >::type
>::type;

struct vec{
    typedef int value_type;
};

BOOST_MPL_ASSERT(( boost::is_same<
    select<mpl::identity<float> >::type, float
> ));

BOOST_MPL_ASSERT(( boost::is_same<
    select<int>::type, int
> ));

BOOST_MPL_ASSERT(( boost::is_same<
    select<vec>::type, vec::value_type
> ));

おまけ Template Aliases で再帰もどき

Template Aliases だと再帰的な処理はできないんですが、無理やりやるとこんな感じ。
素直に struct を使いましょう ._.

[ソース]

#include <boost/mpl/if.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/less_equal.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/minus.hpp>
#include <boost/mpl/apply.hpp>

namespace mpl = boost::mpl;

template<typename T>
struct T_type : T::type{};

template<typename T, typename F>
using fact_impl = mpl::if_<
    mpl::equal_to<T, mpl::int_<1> >,
    T,
    mpl::multiplies<T, T_type<mpl::apply2<F, mpl::minus<T, mpl::int_<1> >, F> > >
>;

template<typename T>
using fact = typename fact_impl<T, fact_impl<mpl::_1, mpl::_2> >::type;

BOOST_MPL_ASSERT(( mpl::equal_to<
    fact<mpl::int_<3> >::type,
    mpl::int_<6>
> ));

お姉妹