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

Using a specialization of variadic template as a template argument

本文关键字:参数 专门化      更新时间:2023-10-16

考虑以下内容:

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

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

EDIT请注意,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实参与对应的模板形参

有不同的模板形参
在这种情况下,没有考虑到部分专门化,因此两个声明不同。
您应该将A声明为:
template <template <class...> class TT = MyT> struct A;

或者在某个地方声明一个约束为单个参数的类型,例如通过using声明

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

第二,模板模板实参是一头奇怪的野兽。如果模板模板实参接受任何可以用模板模板实参中描述的签名实例化的东西,这是有意义的。

不是这样的。

相反,它以另一种方式工作。

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

正确的回应,一旦你明白了,是"什么鬼?"如果你的回答不是"管他的",回头看看你是否能理解。这基本上是从c++中典型的参数类型向后工作的;它的行为更像是返回类型而不是参数。(如果你想了解一些可以让你直接讨论这个问题的语言,请学习术语逆变和协方差)

这是相当不直观的,为什么它以这种方式工作将涉及到追溯c++的历史。

但是,作为一个好处,template<class...>class实参实际上是"任何只接受类型形参的模板"。我觉得这非常有用。

作为一个缺点,template<class>class参数几乎是完全无用的。

Tl;dr:使您的template<template参数为template<template<class...>class,并且只使用只接受类型的模板的元程序。如果您有一个接受值的模板,请编写一个类型包装器,将std::size_t X的需求替换为std::integral_constant< std::size_t, X >

暂时忘记了"你为什么要这样做?"如果你没有做任何模板专门化,第一个版本也可以工作。

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中您总是使用特定的类实例化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;
}

它可以工作,因为模板类TT只接受带有<类……>模板。特化类不依赖于此(因为它的底层仍然是模板<类……>)

B)即使你更新你的结构A到模板

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

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

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