[英]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;
}
错误很明显:
模板模板参数具有与其对应的模板模板参数不同的模板参数
在这种情况下不考虑部分特化,因此两个声明不同。
您应该将A
声明为:
template <template <class...> class TT = MyT> struct A;
或者在某处声明一个约束为单个参数的类型,例如通过使用声明的方式。
首先,默认参数也不起作用。
其次,模板模板争论是一种奇怪的野兽。 如果模板模板参数将采用可以使用模板模板参数中描述的签名进行实例化的任何内容,那将是有意义的。
这不是它的工作原理。
相反,它反过来工作。
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::integral_constant< std::size_t, X >
替换std::size_t X
的需求。
忘了“你为什么要这样做?”的问题,如果你没有做任何模板专业化,第一个版本就可以工作。
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`
};
我想将你的问题分成两部分。
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只接受带有<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;
}
但是,如果您真的无法更新这些定义的签名,则可以执行代理类
template< class T >
struct Proxy : MyT<T>
{
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.