SFINAE的Typename关键字

Typename Keyword for SFINAE

本文关键字:关键字 Typename SFINAE      更新时间:2023-10-16

我在研究现代C++中的SFINAE,我看到了以下代码:

#include <iostream>
struct Bar
{
typedef double it;
};
template <typename T>
typename T::it Foo(const T& arg_f) {
std::cout << "Foo<T>" << std::endl;
return 0;
}
int Foo(int i) { std::cout << "foo(int)" << std::endl; return 0; }
int main(int argc, const char* argv[])
{
Foo(Bar());
Foo(0);
return 0;
}
  1. 为什么在这段代码中,开发人员使用了typename T::it
  2. 那个字体名称和酒吧的结构有什么关系?因为it变量只是在bar结构中定义的,但它在结构外用于函数声明
  3. 什么是SFINAE

此处使用关键字typename是因为您正在访问模板类型参数的类型成员。

除了如果TBar,那么它应该公开一个it类型的成员以获得对重载的访问之外,这是完全无关的。

替换失败不是错误是一种模板元编程模式,它依赖于"删除"无法编译的重载

在您放在这里的模板函数中,开发人员间接指定Foo只是一个使用Bar结构(或其派生实例(的函数。因此,如果你像Foo(Bar(((那样实例化它,编译器推导出的模板函数如下:

Bar::it Foo(const Bar& arg_f) {
std::cout << "Foo<T>" << std::endl;
return 0;
}

但是,如果我们将一个整数值而不是Bar对象传递给函数,它将像以下代码一样被实例化:

int::it Foo(const int& arg_f) {
std::cout << "Foo<T>" << std::endl;
return 0;
}

它有一个错误的实现,因此编译器将失败,因为int类没有它的成员。

然而,如果您想处理这个问题,您应该为int值重载foo函数,如下所示:

int Foo(int arg_f)
{
std::cout << "Foo<int>" << std::endl;
return arg_f;
}

或者,您可以使用enable_if_t为浮点或…等专用数据类型启用模板函数:

template <typename T>
typename std::enable_if_t<std::is_floating_point<T>::value, T> Foo(T t)
{
std::cout << "Foo<floating point>" << std::endl;
return t;
}

此外,我应该澄清类型名称只是区分值和类型。当您使用它时,编译器将该对象视为一种类型,而不是一个值。因此,开发人员使用它来让编译器意识到它是一种类型而不是值。

此外,正如@Vivick所说,"替换失败不是错误"是一种模板元编程模式,它依赖于"删除"无法编译的重载。然而,维基百科为SFINAE提供了一个很好的参考:https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error