SFINAE 的类型名称关键字
Typename Keyword for SFINAE
我正在研究现代 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;
}
- 为什么在这段代码中,开发者使用了 typename T::it?
- 该类型名称与 Bar 的结构有何关系?因为它变量只是在 bar struct 中定义,但它在 struct 之外用于函数声明。
- 到底什么是 SFINAE?
此处使用关键字 typename
,因为您正在访问模板类型参数的类型成员。
除了如果 T
是 Bar
那么它应该公开一个 it
类型的成员以获得对重载的访问之外,它是完全不相关的。
替换失败不是错误 是一种模板元编程模式,它依赖于 "removing" 无法编译的重载
在您放置在这里的模板函数中,开发人员间接指定 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 class 没有它的成员。
但是,如果您想处理这个问题,您应该为 int 值重载 foo 函数,如下所示:
int Foo(int arg_f)
{
std::cout << "Foo<int>" << std::endl;
return arg_f;
}
或者您可以使用 enable_if_t 为特定数据类型启用模板函数,如浮点数或 ...:[=15=]
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 所说,替换失败不是错误是一种模板元编程模式,它依赖于无法编译的 "removing" 重载。然而,维基百科对 SFINAE 有很好的参考:https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error
我正在研究现代 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;
}
- 为什么在这段代码中,开发者使用了 typename T::it?
- 该类型名称与 Bar 的结构有何关系?因为它变量只是在 bar struct 中定义,但它在 struct 之外用于函数声明。
- 到底什么是 SFINAE?
此处使用关键字 typename
,因为您正在访问模板类型参数的类型成员。
除了如果 T
是 Bar
那么它应该公开一个 it
类型的成员以获得对重载的访问之外,它是完全不相关的。
替换失败不是错误 是一种模板元编程模式,它依赖于 "removing" 无法编译的重载
在您放置在这里的模板函数中,开发人员间接指定 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 class 没有它的成员。
但是,如果您想处理这个问题,您应该为 int 值重载 foo 函数,如下所示:
int Foo(int arg_f)
{
std::cout << "Foo<int>" << std::endl;
return arg_f;
}
或者您可以使用 enable_if_t 为特定数据类型启用模板函数,如浮点数或 ...:[=15=]
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 所说,替换失败不是错误是一种模板元编程模式,它依赖于无法编译的 "removing" 重载。然而,维基百科对 SFINAE 有很好的参考:https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error