使用可变参数模板的特化作为模板参数

Using a specialization of variadic template as a template argument

考虑以下几点:

template <class...>
struct MyT;

template <class T>
struct MyT<T> {};

template <template <class> class TT = MyT> struct A {}; // fine

using B = A<MyT>; // does not compile

int main() {
  return 0;
}

MyT 用作 A 的默认参数时,编译器 (g++ 5.4.0) 很满意。但是,当它用于实例化A时,情况就不同了:

temp.cpp:19:16: error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class> class TT> struct A’
 using B = A<MyT>;
                ^
temp.cpp:19:16: note:   expected a template of type ‘template<class> class TT’, got ‘template<class ...> struct MyT’

我可以通过引入别名来修复它:

template <class T>
using MyTT = MyT<T>;

using B = A<MyTT>; // fine

问题:错误原因是什么,有没有不引入别名的解决方案?

编辑 请注意,A 被声明为具有如图所示的模板模板参数,并且未给出更改。

你不能那样做,也不能使用这样的类型作为默认参数。它似乎 接受 只要你不依赖它并不意味着默认参数是有效的。
考虑以下显式使用默认类型的代码:

template <class...>
struct MyT;

template <class T>
struct MyT<T> {};

template <template <class> class TT = MyT> struct A {}; // fine

int main() {
  A<> a;
  return 0;
}

错误很明显:

template template argument has different template parameters than its corresponding template template parameter

在这种情况下不考虑部分特化,因此两个声明不同。
您应该将 A 声明为:

template <template <class...> class TT = MyT> struct A;

或者在某处声明一个限制为单个参数的类型,例如通过 using 声明。

暂时忘记 "Why would you do this?" 的问题, 如果您没有进行任何模板专业化,第一个版本就可以工作。

template <class T>
struct MyT {  };

template <template <class> class TT = MyT> struct A 
{};

using B = A<MyT>;

对于模板特化,编译器必须确定最佳匹配,但由于您实际上从未提供过任何模板参数,所以它是不明确的。

当您引入 MyTT 时,您使用的是单个模板参数,并且编译器足够聪明,可以在只有一个参数时看到您具有特化:

template <class T>
using MyTT = MyT<T>;

在这种情况下,它选择专业化而不是可变参数版本。

但现在我们回到那个宏大的问题……为什么?除非在 A 中你总是用特定的 class 实例化 MyT,否则使用 A 根本没有意义:

template<template<class> class TT = MyT> struct A
{
   // use an instance of TT??
   TT<int> myInstance; // forced to choose `int`
};

我想把你的问题分成两部分。

A) 考虑你的结构模板更简单

template <class T>
  struct TestStruct {
};

template <
  template <class>
  class TT = TestStruct
>
struct A
{
  int a; // sorry to modify this. This help to show how it works
};

int main() {
  A< TestStruct > b;
  b.a; // it works!
  return 0;
}

它之所以有效,是因为模板 class TT 只接受带有 < class... > 模板的模板。专门的 class 不依赖于此(因为它的底层仍然是模板 < class ... > )

B) 即使您将结构 A 更新为模板 < class... > 一个,您仍然还有一个问题。 TT 的模板参数是什么?请看下面的例子

template <class...>                                                                                                                 
struct MyT;                                                                                                                         

template <class T>                                                                                                                  
struct MyT<T> {                                                                                                                     
    int a;                                                                                                                          
};                                                                                                                                  

template <                                                                                                                          
    template <class...>                                                                                                             
    class TT = MyT
    // may be you need to add some more typename here, such as
    // typename T1, ... , and then TT<T1> a;                                                                                                                 
>                                                                                                                                   
struct A                                                                                                                            
{                                                                                                                                   
    TT<int> a; 
    // Here ! TT is a template only, do not have the template parameters!!!                                                                                                                   
};                                                                                                                                  

int main() {                                                                                                                        

  A< MyT > b;                                                                                                                                        
  b.a;     // it works!!                                                                                                                         

  return 0;                                                                                                                         
}                                                                                                                                   

但是,如果你真的不能更新那些定义的签名,你可以做一个代理 class

template< class T >
struct Proxy : MyT<T>
{
};

首先,默认参数也不起作用。

其次,模板模板争论是一个奇怪的野兽。如果模板模板参数接受任何可以用模板模板参数中描述的签名实例化的东西,那将是有意义的。

这不是它的工作原理。

反之亦然。

template<template<class...>class Z> struct foo {};
template<template<class   >class Z> struct bar {};

template<class...>struct a{};
template<class   >struct b{};

foo 将接受 a b.

bar 将接受 b.

一旦你理解了这个问题,正确的反应是"what the hell?"。如果你没有回应 "what the hell" 备份,看看你是否能理解它。这基本上与 C++ 中争论的典型类型相反;它的行为更像是 return 类型而不是参数。 (如果你想看到一些让你直接谈论这个的语言,请学习术语逆变和协变)

这是非常不直观的,为什么它以这种方式工作将涉及追溯 C++ 的史前史。

但是,作为一个好处,template<class...>class 论点实际上是 "any template that only takes type parameters"。我发现这非常有用。

不利的是,template<class>class 争论几乎完全没有用。

Tl;dr:使您的 template<template 参数成为 template<template<class...>class,并且仅使用仅采用类型的模板进行元编程。如果您有一个采用值的模板,请编写一个类型包装器,用 std::integral_constant< std::size_t, X >.

替换 std::size_t X 的要求