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

时间:2016-09-26 12:48:53

标签: c++ c++11 variadic-templates template-specialization

请考虑以下事项:

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被声明为具有模板模板参数,如图所示,并且未提供更改。

4 个答案:

答案 0 :(得分:3)

您不能这样做,也不能将此类型用作默认参数。只要您不依赖它,似乎接受的事实并不意味着默认参数是有效参数。
请考虑以下明确使用默认类型的代码:

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

错误很明显:

  

模板模板参数具有与其对应的模板模板参数

不同的模板参数

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

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

或者在某处声明一个受限于单个参数的类型,例如通过使用声明的方式。

答案 1 :(得分:1)

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

其次,模板模板争论是一种奇怪的野兽。如果模板模板参数将采用可以使用模板模板参数中描述的签名进行实例化的任何内容,那将是有意义的。

这不是它的工作方式。

相反,它反过来工作。

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,将metaprogram设为仅包含类型的模板。如果您有一个采用值的模板,请编写一个类型包装器,用std::size_t X替换std::integral_constant< std::size_t, X >的要求。

答案 2 :(得分:0)

忘了“你为什么要这样做?”的问题, 如果您没有进行任何模板专业化,第一个版本将起作用。

template <class T>
struct MyT {  };

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

using B = A<MyT>;

使用模板特化,编译器必须确定最佳匹配,但由于您实际上没有提供任何模板参数,因此它是不明确的。

当您介绍MyTT时,您使用的是单个模板参数,并且编译器非常智能,只有在只有一个arg时才能看到您具有特化:

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

答案 3 :(得分:0)

我想将您的问题分成两部分。

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只接受模板&lt; class ...&gt;模板。专门化的类不依赖于此(因为它的基础仍然是模板&lt; class ...&gt;)

B)即使您将结构A更新为模板&lt; class ...&gt;一,你还有一个问题。 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;                                                                                                                         
}                                                                                                                                   

但是,如果您真的无法更新这些定义的签名,则可以执行代理类

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