使用多个模板参数包的部分模板特化

16

继续学习可变参数模板,我遇到了另一个问题。

假设有以下模板类:

template < typename T >
struct foo 
{
    //default implementation
};

可以对可变模板实例进行部分特化,例如:

template < template < typename ... > class T, typename ...Args >
struct foo< T< Args... > >
{
    //specialized implementation
};

有了这个,foo< int >将对应于默认实现,foo< std::tuple< int,char >>将对应于专门的实现。

然而,当使用多个模板参数时,情况变得更加复杂。例如,如果我们有以下模板类

template < typename T, typename U >
struct bar {};

如果像我们对foo所做的那样部分专门化它,我们将无法实现。

template < template < typename ... > class T, typename ...TArgs,
           template < typename ... > class U, typename ...UArgs >
struct bar< T< TArgs... >, U< UArgs... > > {};

//This would correspond to the specialized version with
//T=std::tuple,
//TArgs=int,char
//U=std::tuple,
//UArgs=float
bar< std::tuple< int, char >, std::tuple< float > > b;

事实上,如果我没记错的话,在模板参数列表中我们只能有一个模板参数包,并且它必须位于末尾。我理解在模板声明中这是强制性的原因,但对于某些部分模板特化(比如上面的例子),这不应该成为问题。

是否可能使用多个模板参数包来实现部分模板特化?


编辑: 现在我感到有点傻...我上面给出的代码可以完美编译(至少在gcc 4.5下是可以的)。我遇到的编译错误不是因为多个参数包,而是因为它们作为成员函数参数的使用。在bar的部分特化中,我尝试定义一个同时接受TArgsUArgs参数的成员函数:

template < template < typename ... > class T, typename ...TArgs, 
           template < typename ... > class U, typename ...UArgs >
struct bar< T< TArgs... >, U< UArgs... > >
{
    void method( TArgs... targs, UArgs... uargs ) //compile error here
    {
    }
};

在成员函数声明中,gcc给出了错误提示:

参数包必须在参数列表末尾。

据我所知,编译器应该能够为给定的模板实例定义正确的成员函数,例如bar< std::tuple< int, char >, std::tuple< float > >应该包含一个成员函数void method( int, char, float )。我做错了什么吗?或者我试图做一些不可能的事情吗?如果是这样,有没有一个很好的理由说明为什么这不可能?

不错,我不知道你可以将模板列表的一部分或全部指定为模板本身。 - JAB
1个回答

6

也许我的回答不能直接解决你的问题,但是以下代码在我测试的ideone(gcc-4.5.1)上编译成功了。

#include <cstdio>
#include <tuple>

template< class, class > struct S {
  S() { puts("primary"); }
};

template<
  template< class... > class T, class...TArgs
, template< class... > class U, class...UArgs
>
struct S< T< TArgs... >, U< UArgs... > > {
  S() { puts("specialized"); }
};

int main()
{
  S< int, int >  p;                                       // "primary"
  S< std::tuple< int, char >, std::tuple< float > >  s;   // "specialised"
}

我不确定这段代码是否严格符合规范,但根据我阅读的N3225 14.5.3,我找不到提到模板参数包必须是最后一个模板参数的语句。
编辑:
我重新阅读了N3225,并找到了以下声明:
8.3.5/4 如果parameter-declaration-clause以省略号或函数参数包(14.5.3)结尾,则参数数量应等于或大于没有默认参数并且不是函数参数包的参数数量。 14.8.2.5/10 [注意:函数参数包只能出现在parameter-declaration-list(8.3.5)的末尾。-end note]
所以,正如你所提到的,函数参数包必须是最后一个参数,不幸的是。 类模板的非模板成员函数在实例化时(完全特化)成为该类的普通函数。 因此,我希望这个问题中的代码可以逻辑上编译,作为一个特殊情况。

你说得对,代码编译完美!我添加的问题实际上有点不同,我会编辑问题... 对此给您带来的不便深感抱歉... - Luc Touraille
@LucTouraille:别担心 :-) 这对我来说是一次很好的自我学习机会。 - Ise Wisteria
有些东西让我觉得这个引用不适用于此处,因为两个参数包已经是已知的,只需要扩展它们。如果clang接受这段代码,我也不会感到惊讶。 - Johannes Schaub - litb
如果我没记错的话,委员会正在考虑放宽这个规则,并允许在参数列表中仍需要推导的包。在这种情况下,它将只是一个非推导上下文。(在手机上写作,请忽略打字错误) - Johannes Schaub - litb
@JohannesSchaub: “这个引用不适用于此处”
  • 那种解释看起来也很合理。 “委员会正在考虑放宽这个规定”
  • 如果能实现就太好了!
- Ise Wisteria

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接