用于识别class-within-template-class的元函数的麻烦

时间:2012-12-04 17:44:27

标签: c++ templates template-meta-programming boost-mpl

我的代码具有以下结构:

template <typename T>
struct Foo
{
  struct Bar
  {
    int data;
  };
};

我想编写元函数,告诉我一个类型是Foo还是Bar。第一个很简单:

template <typename T>
struct is_foo : boost::mpl::false_
{};

template <typename T>
struct is_foo<Foo<T> > : boost::mpl::true_
{};
...
BOOST_MPL_ASSERT(( is_foo<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_foo<int> ));

但是,同样的方法对Bar不起作用:

template <typename T>
struct is_bar : boost::mpl::false_
{};

template <typename T>
struct is_bar<typename Foo<T>::Bar> : boost::mpl::true_
{};

编译器拒绝此代码。海湾合作委员会说:

main.cpp:38:8: error: template parameters not used in partial specialization:
main.cpp:38:8: error:         ‘T’

奇怪的是,clang会编译代码,但会发出警告并且元函数不起作用(总是错误的):

main.cpp:38:8: warning: class template partial specialization contains a template parameter that can not be deduced;
      this partial specialization will never be used
struct is_bar<typename Foo<T>::Bar> : boost::mpl::true_
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:37:20: note: non-deducible template parameter 'T'
template <typename T>
                   ^

此问题是否有解决方法?特定于c ++ 11的解决方案没问题。

3 个答案:

答案 0 :(得分:1)

问题是TFoo<T>::Bar类型的名称的一部分,但它不属于该类型的结构

一种可能的解决方案是在类型结构中编码T

template<typename Outer, typename Inner> struct Nested: public Inner {
  using Inner::Inner;
};
template<typename T> struct Foo {
  struct BarImpl {
    int data;
  };
  using Bar = Nested<Foo<T>, BarImpl>;
};

template <typename T> struct is_bar: std::false_type {};
template <typename T, typename U> struct is_bar<Nested<Foo<T>, U>>:
  std::is_same<typename Foo<T>::Bar, Nested<Foo<T>, U>> {};

测试:

static_assert(is_bar<Foo<int>::Bar>::value, "!");
static_assert(!is_bar<Foo<int>>::value, "!");
static_assert(!is_bar<int>::value, "!");

答案 1 :(得分:1)

使用TTI(http://svn.boost.org/svn/boost/sandbox/tti),对于我自己的问题,这是一个非常不优雅的解决方案:

首先,向Bar添加一个虚拟标签:

template <typename T>
struct Foo
{
  struct Bar
  {
    typedef void i_am_bar;
    int data;
  };
};

接下来,使用TTI检查该标记:

BOOST_TTI_HAS_TYPE(i_am_bar);

template <typename T>
struct is_bar : boost::tti::has_type_i_am_bar<T>
{};
...
BOOST_MPL_ASSERT(( is_bar<Foo<int>::Bar> ));
BOOST_MPL_ASSERT_NOT(( is_bar<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_bar<int> ));

有点可惜,但它满足了我的用例。

答案 2 :(得分:0)

编译器是正确的,简单易懂的解释是:他们只是不想替换所有可能的类型T只是为了实现给定模板中是否存在嵌套类型吧。您可以在关于模板的“经典”(以及众所周知的希望)书中找到更精确的解释:“C ++模板 - 完整指南”

幸运的是,C ++ 11可以帮助您做得更好! :)

#include <type_traits>

template <typename T>
struct has_nested_bar
{
    template <typename W>
    struct wrapper {};

    template <typename C>
    static std::true_type check(
        const wrapper<C>*
      , const typename C::Bar* = nullptr
      );

    template <class C>
    static std::false_type check(...);

    constexpr static bool value = std::is_same<
        decltype(check<T>(nullptr))
      , std::true_type
      >::type::value;
    typedef std::integral_constant<bool, value> type;
};

此metafucntion将检查给定类型是否具有嵌套的Bar类型(据我所知,它是您is_bar的初始豁免)。

template <typename T>
struct Foo
{
    struct Bar
    {
        int data;
    };
};

struct Bar {};

int main()
{
    std::cout << has_nested_bar<Foo<int>>::value << std::endl;
    std::cout << has_nested_bar<Bar>::value << std::endl;
    return 0;
}

将输出:

zaufi@gentop /work/tests $ ./has-nested-bar
1
0

稍后您可以将此元功能与is_foo结合使用,以检查嵌套Bar实际上是否位于Foo ...