SFINAE 的模板专用化



我已经使用模板有一段时间了,但最近我遇到了一个奇怪的SFINAE模板编程。 我的问题是我们为什么要写typename = {something}作为C++中的模板参数?

这就是SFINAE的工作方式;)

如您所知,您必须在模板声明中"创建失败",而不是在模板定义中。如:

template < typename X, typename = ... here is the code which may generate an error     during instantiation >
void Bla() {}

在声明中放置一些"代码"的唯一机会是在模板参数列表或模板函数声明本身中定义一些内容,例如:

template < typename X>
void Bla( ... something which may generates an error ) {}

例:

template <typename T, typename = std::enable_if_t< std::is_same_v< int, T>>>
void Bla()
{
std::cout << "with int" << std::endl;
}
template <typename T, typename = std::enable_if_t< !std::is_same_v< int, T>>>
void Bla(int=0)
{
std::cout << "with something else" << std::endl;
}
int main()
{
Bla<int>();
Bla<std::string>();
}

但这里"制造换人失败"的背景是什么?

诀窍在std::enable_if后面的某个地方.我们也可以在没有它的情况下使用它:

template <typename T, typename = char[ std::is_same_v< int, T>]>
void Bla()
{   
std::cout << "with int" << std::endl;
}   
template <typename T, typename = char[ !std::is_same_v< int, T>]>
void Bla(int=0)
{   
std::cout << "with something else" << std::endl;
}   

看看:typename = char[ !std::is_same_v< int, T>]在这里,std::is_same_v给了我们一个布尔值,如果无效,则转换为 0,如果有效,则转换为任何正数。创建char[0]只是 c++ 中的一个错误!因此,通过用!否定我们的表达式,我们得到了一个"is int"和一个"is not int"。一旦它尝试创建一个大小为 0 的数组,这是一个失败,模板不会被实例化,一旦它生成了有效表达式的类型char[!0]

最后一步:

... typename = ...

意思是:将定义一个类型,这里没有模板参数名称,因为参数本身以后不会使用。你也可以写:

... typename X = ... 

但由于不使用 X,请离开它!

摘要:您必须提供一些代码,这些代码是否会生成错误,具体取决于给定表达式的类型或值。表达式必须是函数声明的一部分或模板参数列表的一部分。不允许在函数/类定义中放置错误,因为这不再是 SFINAE 意义上的"非错误"。

更新:SFINAE 表达式的结果可以用于进一步的表达式吗:是

例:

template < typename TYPE >
void CheckForType()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
template <typename T, typename X = std::enable_if_t< std::is_same_v< int, T>, float>>
void Bla()
{
std::cout << "with int" << std::endl;
CheckForType<X>();
}
template <typename T, typename X = std::enable_if_t< !std::is_same_v< int, T>, double >>
void Bla(int=0)
{
std::cout << "with something else" << std::endl;
CheckForType<X>();
}
int main()
{
Bla<int>();
Bla<std::string>();
}

输出:

with int
void CheckForType() [with TYPE = float]
with something else
void CheckForType() [with TYPE = double]

解释:std::enable_if有第二个模板参数用作返回类型,如果它的第一个参数将true。因此,您可以在此处使用该类型。如您所见,函数CheckForType是使用此定义的类型从 SFINAE 表达式中X调用的。

一个例子:

//for enum types.
template <typename T, typename std::enable_if<std::is_enum_v<T>, int>::type = 0>
void Foo(T value)
{
//stuff..
}

我只希望在T是枚举类型时选择此Foo。我们可以使用替代失败不是错误(SFINAE(的规则来实现这一点。


此规则指出,如果在替换过程中出现故障,则不应是编译错误。编译器应仅消除此替换的函数。

那么,我们如何确保仅使用枚举类型调用Foo呢?嗯,这很容易,我们找到了触发替换失败的方法!

如果我们检查 cpp首选项std::enable_if它说:

template< bool B, class T = void >
struct enable_if;

如果Btrue,则std::enable_if有一个公共成员类型定义type,等于 到T;否则,没有成员类型定义。

这意味着,如果std::is_enum<T>::valuetrue(T的枚举类型就是这种情况(,那么B将被true,因此type将是一个有效的类型,一个指定的int

但是,如果std::is_enum::valuefalse那么B将被false,因此type甚至不存在。在这种情况下,代码试图在::type不存在的情况下使用它,因此这是一个替换错误。因此,SFINAE启动并将其从候选列表中删除。


我们使用= 0的原因是提供一个默认的模板参数值,因为我们实际上对使用此类型或其值并不感兴趣,我们只使用它来触发 SFINAE。

此构造是随默认参数提供的无名称模板参数。通常,模板参数如下所示

typename <identifier> [ = <type> ]

但可以省略标识符。

您可以在 SFINAE 模板中经常看到这种构造,因为当且仅当某些条件成立或某些代码段有效时,这是一种启用专用化的便捷方法。因此,人们经常可以看到

template <typename T, typename = std::enable_if<(some-constexpr-involving-T)>::type> ...

如果表达式碰巧计算为 true,则一切都很好:std::enable_if<true>::type只是void,我们有一个很好的工作模板专业化。

现在,如果上面的表达式恰好为 false,std::enable_if<false>没有名为type的成员,则会发生替换失败(SFINAE 中的 SF(,并且不考虑模板专用化。

tge参数的作用到此结束。它不用于模板本身中的任何内容,因此它不需要名称。

相关内容

  • 没有找到相关文章

最新更新