在实施类型列表时苦苦挣扎

ele*_*kil 6 c++ templates metaprogramming template-meta-programming c++11

出于教育目的,我想写自己c++11的类型列表.裸列表看起来像这样:

template <typename ... Ts> struct type_list;

template <typename T, typename ... Ts>
struct type_list<T, Ts ...> {
    typedef T Head;
    typedef type_list<Ts ...> Tail;
};

template <typename T> struct type_list<T> {
     typedef T Head;
     typedef null_type Tail;
};
Run Code Online (Sandbox Code Playgroud)

我创建了一个调用front提取第一个元素的函数:

template <typename T> struct front;

template <typename TypeList>
struct front {
    typedef typename TypeList::Head type;
};
Run Code Online (Sandbox Code Playgroud)

哪个按预期工作,即此代码

typedef type_list<int> lst;
typedef type_list<float,int> lst2;
typedef type_list<double,float,int> lst3;
typedef type_list<char,double,float,int> lst4;

std::cout << "front(lst1): " << typeid( front<lst>::type ).name() << std::endl;
std::cout << "front(lst2): " << typeid( front<lst2>::type ).name() << std::endl;
std::cout << "front(lst3): " << typeid( front<lst3>::type ).name() << std::endl;
std::cout << "front(lst4): " << typeid( front<lst4>::type ).name() << std::endl;
Run Code Online (Sandbox Code Playgroud)

生产:

front(lst1):i
front(lst2):f
front(lst3):d
front(lst4):c

当然,back功能是下一步,但是,我似乎无法让它工作.我的代码

template <typename T> struct back;

template <typename TypeList>
struct back {
    typedef typename std::conditional<std::is_same<typename TypeList::Tail, null_type>::value,
                      typename TypeList::Head,
                  typename back<typename TypeList::Tail>::type>::type type;
};
Run Code Online (Sandbox Code Playgroud)

不编译(clang 3.2)[ lst定义为之前]:

TypeList.cc:33:71: error: no type named 'Tail' in 'null_type'
  typedef typename std::conditional<std::is_same<typename TypeList::Tail, null_type>::value,
                                                 ~~~~~~~~~~~~~~~~~~~^~~~
TypeList.cc:35:20: note:
  in instantiation of template class 'back<null_type>' requested here
    typename back<typename TypeList::Tail>::type>::type type;
    ^

TypeList.cc:54:44: note:
  in instantiation of template class 'back<type_list<int> >' requested here
    std::cout << "back(lst1): " << typeid( back<lst>::type ).name() << std::endl;
                                           ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

  • 为什么不std::conditional触发?

Fil*_*efp 7

使用不当 std::conditional

std::conditonal<condition, true-type, false-type>
Run Code Online (Sandbox Code Playgroud)

你的问题可以归结为,无论是道道通假型std::conditional必须产生一个有效的名称,无论哪一方面的条件选秀权.

注意:如果不需要完整的解释,本文末尾有一个建议的解决方案.


考虑以下示例:

struct A { typedef int type; };
struct B { /* empty */ };
Run Code Online (Sandbox Code Playgroud)

template<class T>
struct some_trait {
  typedef typename std::conditional<
    /*  condition -> */ std::is_same<T, A>::value,
    /*  true-type -> */ typename T::type,
    /* false-type -> */ void
  >::type result;
};
Run Code Online (Sandbox Code Playgroud)

实例化some_trait<A>将是完全有效的,但是如果我们实例化它会发生什么B

template<>
struct some_trait<B> {
  typedef typename std::conditional<
    std::is_same<B, A>::value,
    typename B::type,  // (A), ill-formed
    void
  >::type result;
};
Run Code Online (Sandbox Code Playgroud)

在上面的,我们假装是一个编译器,我们更换的每一次出现TB,这不是所有的辛苦工作,但它已经提出了与我们的一个非常重要的问题主要模板.

当编译器实例some_trait<T>T = B真型在我们的std::conditional将是B::type (A) .

但由于B内部没有名称,type我们将得到一个编译诊断程序,说我们的代码有问题,即; 我们正在尝试访问不存在的名称.

foo.cpp:15:37: error: no type named 'type' in 'B'
    /*  true-type -> */ typename T::type, // (A), ill-formed
Run Code Online (Sandbox Code Playgroud)

提出的解决方案

毫无疑问,我们必须做些什么,并简而言之; 阻止我们的模板访问可能不存在的名称.

这样做的一个简单方法是依赖显式特化,而不是使用std::conditional.


示例实现 back

template<typename TypeList>
struct back {
  typedef typename back<typename TypeList::Tail>::type type;
};

template<typename T>
struct back<type_list<T>> {
  typedef typename type_list<T>::Head type;
};
Run Code Online (Sandbox Code Playgroud)

注意:如果的实例template<typename T> struct back;type_list,只有一个参数,我们知道我们是在最后一个节点.