2011-07-20 19 views
5

目前,我试图让一些代码对不同类型做出不同的反应。这不是确切的代码,但它可以传递信息。带有可变参数的元组内容的部分专精

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; 
} 

我已经尝试超过这个代码显示,但没有到目前为止的工作,我在非命名空间范围横跨问题跑了明确的分工。作为参考,我正在研究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...>>; 

回答

13

如果您编译使用clang,它有益报道,(2)和(3)不可用。您希望选择的(3)的警告如下:

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

struct alpha<std::tuple<Args..., T>, T> { 
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

注:非推断出模板参数 'Args'

template<class T, class... Args> 
         ^

为什么Args不抵扣? C++ 0x FDIS在§14.8.2.5/ 9中声明:

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

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

std::tuple的参数列表是Args出现在模板特化的参数列表中的唯一位置;因为它不是从那里推导出来的,它根本不能被推导出来,专业化也不会被使用。

Matthieu M. provides a clever workaround in his answer

+0

我不知道“解决方案”是什么或者是否有一个。虽然这里还有其他人会知道。 –

+0

至少,这告诉我,没有可能的解决方案使用这种方法,并让我转移到其他人。如果我想出一个我喜欢的,我会发布它。 – norcalli

+0

鉴于这是问题,为什么不在左侧展开包装? – dascandy

12

@詹姆斯提供了原因,现在让我们尝试找到一个替代方案。

我会建议使用另一个间接级别。

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; 
}; 

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; } 
}; 

3挂钩起来

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 }; 
}; 

注意,没有最后一类为空元组,所以我不得不在单独的专业化中处理它们。

+0

有趣的是,我正要做一些非常相似的事情,试图得到最后一个论点。我如何给你正确的答案?我是新来的stackoverflow。 – norcalli

+0

@Norcalli:这是不可能的,你必须选择一个或另一个。我建议你在解释你遇到的问题(这是更一般的)之后,让詹姆斯保持选择状态,我的答案是专门为你的用例量身定做的,因此对未来的读者不太感兴趣。 –

+0

非常好...... –

1

如果你想了解一个元组作为一个特定的最后一个成员,这里应该是一个类型的特点:

#include <type_traits> 
#include <tuple> 

template <typename ...Args> struct back; 
template <typename T, typename ...Args> struct back<T, Args...> 
    { typedef typename back<Args...>::type type; }; 
template <typename T> struct back<T> 
    { typedef T type; }; 


template <typename...> struct tuple_has_last : public std::false_type {}; 
template <typename T, typename... Args> struct tuple_has_last<T, std::tuple<Args...>> 
{ 
    static const bool value = std::is_same<typename back<Args...>::type, T>::value; 
}; 

编辑:哦,我没有看到,马修已经写了完全一样的东西。没关系。