C++ 基于类成员存在/不存在的SFINAE
我对SFINAE有基本的了解,例如C++ 基于类成员存在/不存在的SFINAE,c++,c++11,sfinae,C++,C++11,Sfinae,我对SFINAE有基本的了解,例如如何在工作时启用。我最近遇到过,我花了一个多小时试图理解它是如何实际工作的,但没有用 此代码的目标是根据类中是否有特定成员来重载函数。这是复制的代码,它使用C++11: template <typename T> struct Model { vector<T> vertices; void transform( Matrix m ) { for(auto &&vertex : v
如何在工作时启用。我最近遇到过,我花了一个多小时试图理解它是如何实际工作的,但没有用
此代码的目标是根据类中是否有特定成员来重载函数。这是复制的代码,它使用C++11:
template <typename T> struct Model
{
vector<T> vertices;
void transform( Matrix m )
{
for(auto &&vertex : vertices)
{
vertex.pos = m * vertex.pos;
modifyNormal(vertex, m, special_());
}
}
private:
struct general_ {};
struct special_ : general_ {};
template<typename> struct int_ { typedef int type; };
template<typename Lhs, typename Rhs,
typename int_<decltype(Lhs::normal)>::type = 0>
void modifyNormal(Lhs &&lhs, Rhs &&rhs, special_) {
lhs.normal = rhs * lhs.normal;
}
template<typename Lhs, typename Rhs>
void modifyNormal(Lhs &&lhs, Rhs &&rhs, general_) {
// do nothing
}
};
模板结构模型
{
向量顶点;
空洞变换(矩阵m)
{
用于(自动&顶点:顶点)
{
vertex.pos=m*vertex.pos;
modifyNormal(顶点,m,特殊_());
}
}
私人:
结构通用{};
特殊结构:一般结构{};
模板结构int{typedef int type;};
模板
无效修改正常(左侧和左侧、右侧和右侧、特殊){
lhs.normal=rhs*lhs.normal;
}
模板
无效修改法线(左侧和左侧、右侧和右侧、常规){
//无所事事
}
};
就我的一生而言,我无法理解这种机制是如何工作的。具体来说,typename int\\::type=0
对我们有什么帮助,为什么我们在这个方法中需要一个额外的类型(special
/general
)。special
/general
是用来让编译器区分这两个方法的类型。请注意,没有使用第三个参数。一般实现不做任何事情,但在特殊情况下,它会修改正常的。
还请注意,特殊
是从常规
派生而来的。这意味着,如果未定义modifyNormal
的专用版本(SFINAE),则一般情况适用;如果存在专用版本,则将选择它(更具体)
现在在modifyNormal
的定义中有一个开关;如果类型(模板的第一个参数)没有名为normal
的成员,则模板将失败(SFINAE,不要抱怨它,这是SFINAE的诀窍),并且将应用modifyNormal
的其他定义(一般情况)。如果该类型定义了名为normal
的成员,则模板的第三个参数可以解析为附加的第三个参数模板(默认值为0的int
)。第三个参数对函数没有任何作用,仅用于SFINAE(模式应用于它)
为什么在这个方法中需要一个额外的类型(特殊的
/常规的
)
这些函数仅用于允许使用不同的实现重载modifyNormal
函数。它们的特殊之处在于special
使用is-A关系,因为它继承自general
。此外,transform
函数始终调用modifyNormal
重载,该重载采用特殊类型,请参见下一部分
typename int\uuu::type=0
对我们有什么帮助
这是一个具有默认值的模板参数。默认值存在,因此transform
函数不必指定它,这一点很重要,因为另一个modifyNormal
函数没有此模板参数。此外,此模板参数仅用于调用SFINAE
当用推断的类型替换模板参数失败时,将从重载集中丢弃专门化,而不是导致编译错误
<> >如果发生故障,<代码>修改为正常/<代码>函数,将“<代码>特殊代码< /代码>类型从重载集合中移除以考虑。这只剩下modifyNormal
函数采用general
类型,因为special
是-ageneral
类型,所以一切仍然有效
如果未发生替换故障,则将使用使用特殊类型的modifyNormal
功能,因为这是更好的匹配
注意:常规类型是一个struct
,因此默认情况下继承是public
,允许is-a关系而不使用public
关键字
编辑:
你能解释一下为什么我们首先要使用复杂的typename int_216;::type
机制吗
如上所述,这用于触发SFINAE行为。然而,当你把它分解的时候,它不是很复杂。它的核心是为某个类型的T
实例化int\ucode>结构的实例,并定义了type
数据类型:
int_<T>::type
最后,用于实例化int\uu
结构的实际类型是什么?这是由decltype(Lhs::normal)
确定的,它报告了Lhs::normal
的类型。如果typeLhs
type有一个normal
数据成员,那么一切都会成功。但是,如果没有,则会出现替换失败,其重要性已在上文中解释。类型general
和special
用于强制编译器在尝试解析调用时选择第一个函数(更好的匹配)作为第一次尝试。
请注意,呼叫是:
modifyNormal(vertex, m, special_());
无论如何,因为general
继承自special
,所以两者都是有效的,如果第一个在类型替换期间失败,将选择第二个
所以,这就像是说——让我们试一下,好像这个方法确实存在,但是保持冷静,因为我们有一个什么都不做的全面回调
为什么会失败?
这就是int\uu
参与游戏的地方。
如果decltype
(让我说)由于Lhs
中缺少成员方法normal
而给出错误,int\u
无法专门化,替换实际上失败了,但由于SFINAE,此失败不是一个错误
modifyNormal(vertex, m, special_());