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 天后从头开始写同样的东西,我可能会失败。
原因是我不完全理解这里使用的句法和语言延伸。
有人可以解释以下语法的含义吗?

  1. Check<int Fallback::*, &U::find>*(我知道它试图从 SFIANE 中获益,但这如何检测 find 的存在,我相信这也与第二个问题有关)
  2. 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 表示模板参数是

  1. 普通型U
  2. 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