template<typename T, T> 是什么意思?
What does template<typename T, T> mean?
我正在阅读这个史前元程序示例来检测 class 是否支持成员查找。 (或任何其他成员)。
template<typename T>
class DetectFind
{
struct Fallback { int find; };
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
typedef char Yes[1];
typedef char No[2];
template<typename U>
static No& func(Check<int Fallback::*, &U::find>*);
template<typename U>
static Yes& func(...);
public:
typedef DetectFind type;
enum { value = sizeof(func<Derived>(0)) == sizeof(Yes) };
};
int main()
{
std::cout << DetectFind<std::vector<int> >::value << std::endl;
std::cout<< DetectFind<std::set<int> >::value << std::endl;
}
直觉上我明白这背后的目的,但如果有人让我在 10 天后从头开始写同样的东西,我可能会失败。
原因是我不完全理解这里使用的句法和语言延伸。
有人可以解释以下语法的含义吗?
Check<int Fallback::*, &U::find>*
(我知道它试图从 SFIANE 中获益,但这如何检测 find 的存在,我相信这也与第二个问题有关)
template<typename U, U> struct Check;
程序按预期输出0 1;
template<typename U, U>
表示有两个模板参数:一个任意类型 U 和一个类型为 U 的未命名非类型模板参数(例如值参数)。例如,int
场景将是 ClassName<int,42>
.
在您的示例中,类型 U
是指向 int
成员的指针,值是 int
成员的地址。
我们先来看一下struct Check
的声明;
template<typename U,U> struct Check
表示模板参数是
- 普通型
U
U
类型的对象,没有名称。
Check<int Fallback::*, &U::find>*
有点混乱。
第一个模板参数是 pointer to member.,在本例中它代表一个指向 class Fallback
类型 int
的成员的指针,它允许我们写一些东西像这样。
Fallback obj{10};
int Fallback::* find_ptr = &Fallback::find;
std::cout << obj.*find << std::endl; //prints 10, note: ".*" is a separate operator
模板的第二个参数是 class U
成员的地址,正如 struct Check
的初始声明所建议的那样。
首先,让我们考虑结构Derived
。由于它派生自 Fallback
,它肯定包含一个 int 字段 find
,并且可能包含一个成员函数 find
,它的存在就是您要检查的内容。
如上面的答案所述,在 struct Check
的声明中,第一个模板参数是类型,第二个是非类型参数,其类型由第一个参数指定。
鉴于此,让我们检查 func
的两个重载。第一个重载采用指向 Check
结构的指针,其第一个模板参数的类型等于 Fallback
(int Fallback::*
) 的指针指向 int 成员。然后,第二个模板参数被解释为值为 &U::find
的指向 int 成员的指针。
给定 U = Derived
,如果 T
包含一个 find
成员函数,Check
的第二个参数是不明确的,因为它也可以引用 int find
继承自 Fallback
。通过 SFINAE,func
的重载将被丢弃。
第二个 func
重载总是定义明确。但是如果第一个重载没有被丢弃,第二个重载的专业性较低,所以编译器会选择第一个重载。
综上所述:如果template <typename U> func
中的U
包含一个成员函数find
,编译器会选择func
的第二次重载。如果成员函数find
不存在,编译器选择第一个重载。
最后,DetectFind
的值由所选 func
的返回类型的大小决定,这取决于重载是大小为 1 或 2 的 char 数组。从那里,你会得到选择了 func
的哪个重载,并且从上面的讨论中,T
是否有成员函数 find
。
我正在阅读这个史前元程序示例来检测 class 是否支持成员查找。 (或任何其他成员)。
template<typename T>
class DetectFind
{
struct Fallback { int find; };
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
typedef char Yes[1];
typedef char No[2];
template<typename U>
static No& func(Check<int Fallback::*, &U::find>*);
template<typename U>
static Yes& func(...);
public:
typedef DetectFind type;
enum { value = sizeof(func<Derived>(0)) == sizeof(Yes) };
};
int main()
{
std::cout << DetectFind<std::vector<int> >::value << std::endl;
std::cout<< DetectFind<std::set<int> >::value << std::endl;
}
直觉上我明白这背后的目的,但如果有人让我在 10 天后从头开始写同样的东西,我可能会失败。
原因是我不完全理解这里使用的句法和语言延伸。
有人可以解释以下语法的含义吗?
Check<int Fallback::*, &U::find>*
(我知道它试图从 SFIANE 中获益,但这如何检测 find 的存在,我相信这也与第二个问题有关)template<typename U, U> struct Check;
程序按预期输出0 1;
template<typename U, U>
表示有两个模板参数:一个任意类型 U 和一个类型为 U 的未命名非类型模板参数(例如值参数)。例如,int
场景将是 ClassName<int,42>
.
在您的示例中,类型 U
是指向 int
成员的指针,值是 int
成员的地址。
我们先来看一下struct Check
的声明;
template<typename U,U> struct Check
表示模板参数是
- 普通型
U
U
类型的对象,没有名称。
Check<int Fallback::*, &U::find>*
有点混乱。
第一个模板参数是 pointer to member.,在本例中它代表一个指向 class Fallback
类型 int
的成员的指针,它允许我们写一些东西像这样。
Fallback obj{10};
int Fallback::* find_ptr = &Fallback::find;
std::cout << obj.*find << std::endl; //prints 10, note: ".*" is a separate operator
模板的第二个参数是 class U
成员的地址,正如 struct Check
的初始声明所建议的那样。
首先,让我们考虑结构Derived
。由于它派生自 Fallback
,它肯定包含一个 int 字段 find
,并且可能包含一个成员函数 find
,它的存在就是您要检查的内容。
如上面的答案所述,在 struct Check
的声明中,第一个模板参数是类型,第二个是非类型参数,其类型由第一个参数指定。
鉴于此,让我们检查 func
的两个重载。第一个重载采用指向 Check
结构的指针,其第一个模板参数的类型等于 Fallback
(int Fallback::*
) 的指针指向 int 成员。然后,第二个模板参数被解释为值为 &U::find
的指向 int 成员的指针。
给定 U = Derived
,如果 T
包含一个 find
成员函数,Check
的第二个参数是不明确的,因为它也可以引用 int find
继承自 Fallback
。通过 SFINAE,func
的重载将被丢弃。
第二个 func
重载总是定义明确。但是如果第一个重载没有被丢弃,第二个重载的专业性较低,所以编译器会选择第一个重载。
综上所述:如果template <typename U> func
中的U
包含一个成员函数find
,编译器会选择func
的第二次重载。如果成员函数find
不存在,编译器选择第一个重载。
最后,DetectFind
的值由所选 func
的返回类型的大小决定,这取决于重载是大小为 1 或 2 的 char 数组。从那里,你会得到选择了 func
的哪个重载,并且从上面的讨论中,T
是否有成员函数 find
。