具有可变参数的元组内容的部分特化

nor*_*lli 5 c++ templates tuples variadic-templates c++11

目前,我正在尝试获取一些代码以对不同类型做出不同反应.这不是确切的代码,但它会传达消息.

template<class A, class B>
struct alpha {
  enum { value = 0 };
};

template<class T, class... Args>
struct alpha<std::tuple<Args...>, T> {
  enum { value = 1 };
};

// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., std::vector<T> >, T> {
  enum { value = 2 };
};

// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., T>, T> {
  enum { value = 3 };
};

template<class T, class... Args>
struct alpha<T, std::tuple<Args...> > {
  enum { value = 4 };
};

template<class... LArgs, class... RArgs>
struct alpha<std::tuple<LArgs...>, std::tuple<RArgs...> > {
  enum { value = 5 };
};

int main(int argc, char* argv[]) {
  std::cout << alpha<std::tuple<int, double>, double>::value << std::endl; // prints 1
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我尝试了比这个代码更多的尝试,但到目前为止没有任何工作,我在非命名空间范围内遇到了显式特化的问题.作为参考,我正在研究gcc 4.6(oneiric服务器附带的那个),我相信它具有完整的可变参数模板支持.如果实现检测参数包的最后一个参数以及其他类型,我不在乎它有多难看.有什么建议?

编辑:我想根据答案分享我使用的解决方案(这是一个例子).

template<typename T> struct tuple_last;

template<typename T, typename U, typename... Args>
struct tuple_last<std::tuple<T,U,Args...>> {
  typedef typename tuple_last<std::tuple<U,Args...>>::type type;
};

template<typename T>
struct tuple_last<std::tuple<T>> {
  typedef T type;
};

namespace details {
// default case:
template<class T, class U>
struct alpha_impl {
enum { value = 1 };
};

template<class T>
struct alpha_impl<T, T> {
enum { value = 101 };
};

template<class T>
struct alpha_impl<T, std::vector<T>> {
enum { value = 102 };
};

// and so on.
}

template<class T, class... Args>
struct alpha<std::tuple<Args...>, T>
  : details::alpha_impl<T, tuple_last<std::tuple<Args...>>;
Run Code Online (Sandbox Code Playgroud)

Jam*_*lis 13

如果使用clang进行编译,则会有助于报告(2)和(3)无法使用.您希望选择的(3)警告如下:

警告:类模板部分特化包含一个无法推导出的模板参数; 永远不会使用这种部分专业化

struct alpha<std::tuple<Args..., T>, T> {
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

注意:不可导出的模板参数' Args'

template<class T, class... Args>
                           ^
Run Code Online (Sandbox Code Playgroud)

为什么Args不可扣除?C++ 0x FDIS状态见§14.8.2.5/ 9:

如果[根据模板参数指定的类型]的模板参数列表包含不是最后一个模板参数的包扩展,则整个模板参数列表是非推导的上下文.

在你的专业化,类型std::tuple<Args..., T>是在模板参数方面指定的类型ArgsT.它包含一个包扩展(Args...),但该包扩展不是最后一个模板参数(T是最后一个模板参数).因此,tuple(整体<Args..., T>)的整个模板参数列表是非推断的上下文.

参数列表std::tuple是模板特化的参数列表中唯一Args出现的位置; 因为它不能从那里推导出来,所以它根本无法推断,专业化永远不会被使用.

Matthieu M. 在他的回答中提供了一个聪明的解决方法.


Mat*_* M. 12

@James提供了原因,现在让我们尝试寻找替代方案.

我建议使用另一层次的间接.

1.获得最后一个论点

template <typename T> struct Last;

template <typename T, typename U, typename... Args>
struct Last<std::tuple<T,U,Args...>>
{
  typedef typename Last<std::tuple<U,Args...>>::type type;
};

template <typename T>
struct Last<std::tuple<T>>
{
  typedef T type;
};
Run Code Online (Sandbox Code Playgroud)

2.介绍一个专门的帮手

template <typename T, typename U>
struct alpha_tuple
{
  enum { value = 1 };
};

template <typename T>
struct alpha_tuple<T,T>
{
  enum { value = 3 };
};

template <typename T>
struct alpha_tuple<std::vector<T>,T>
{
  enum { value = 2; }
};
Run Code Online (Sandbox Code Playgroud)

把它连起来

template <typename T>
struct alpha<std::tuple<>, T>
{
  enum { value = 1 };
};

template <typename T, typename U, typename Args...>
struct alpha<std::tuple<U, Args...>, T>
{
  typedef typename Last<std::tuple<U, Args...>>::type LastType;
  enum { value = alpha_tuple<LastType,T>::value };
};
Run Code Online (Sandbox Code Playgroud)

请注意,空元组没有最后一个类型,因此我必须在单独的专门化中处理它们.