Naw*_*waz 4 c++ sfinae template-meta-programming
我试图理解下面的类模板是如何工作的(取自这里),但我无法理解它:
template <typename Type>
class has_member
{
class yes { char m;};
class no { yes m[2];};
struct BaseMixin
{
void operator()(){}
};
struct Base : public Type, public BaseMixin {};
template <typename T, T t> class Helper{};
template <typename U>
static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0);
static yes deduce(...);
public:
static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));
};
Run Code Online (Sandbox Code Playgroud)
更具体地说,我不了解其中的目的BaseMixin和存在operator().而且,既然Base是源自它,我也不理解它.
更具体地说,当模板参数Type定义时operator(),为什么只有 SFINAE被触发,导致第一个deduce()函数被忽略而第二个函数被选中?
无论如何,这是我的测试代码:
struct A{}; //SFINAE is triggered for A
struct B{ void operator()(){} }; //SFINAE is not triggered for B
struct C{ void operator()(int,int){} }; //SFINAE is not triggered for C
int main()
{
std::cout << std::boolalpha; //enable true/false instead of 1/0!
std::cout << has_member<A>::result << std::endl;
std::cout << has_member<B>::result << std::endl;
std::cout << has_member<C>::result << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出(ideone):
false
true
true
Run Code Online (Sandbox Code Playgroud)
BaseMixin有一个operator()定义.Base派生自两个Type和BaseMixin,所以如果Type有一个operator()名称查找Base::operator()将是不明确的.deduce被要求Base*,而不是Type*.Helper<void (BaseMixin::*)(), &U::operator()>只有在&U::operator()明确解析时才会实例化BaseMixin::operator().Helper<void (BaseMixin::*)(), &U::operator()>没有没有实例,这是因为Type有自己operator()的制作名称查找&U::operator()不明确,因此过载deduce返回类型yes选择.关于第二个项目的标准引用 - C++11§10.2/ 2-6:
2以下步骤定义
f类范围中成员名称的名称查找结果C.3 查找设置为
f在C被称为S(F,C) ,包括两个部件组:所述声明集,一组命名的成员f; 和子对象集,一组子对象,其中发现了这些成员的声明(可能包括使用声明).在声明集中,using-declaration被它们指定的成员替换,类型声明(包括inject-class-names)被它们指定的类型替换.S(f,C)计算如下:4如果
C包含名称的声明f,该声明集包含的每一个声明f宣布C满足在其中查找出现的语言结构的要求.[ 注意:例如,在elaborated-type-specifier或base-specifier中查找名称时,忽略所有非类型声明,而在嵌套名称说明符中查找名称会忽略函数,变量和枚举器声明.另一个例子是,在using声明中查找名称包括一个类或枚举的声明,该声明通常会被同一范围内该名称的另一个声明隐藏.- 结束注释 ]如果生成的声明集不为空,则子对象集包含C自身,并且计算完成.5否则(即,
C不包含声明f或结果声明集为空),S(f,C)最初为空.如果C具有基类,则计算f每个直接基类子对象B i中的查找集,并将每个这样的查找集S(f,B i)依次合并为S(f,C).6以下步骤定义将查找集S(f,B i)合并到中间S(f,C)的结果:
- 如果S(f,B i)的每个子对象成员是S(f,C)的至少一个子对象成员的基类子对象,或者如果S(f,B i)为空,则S(f) ,C)没有变化,合并完成.相反,如果S(f,C)的每个子对象成员是S(f,B i)的至少一个子对象成员的基类子对象,或者如果S(f,C)为空,则新的S(f,C)是S(f,B i)的副本.
- 否则,如果S(f,B i)和S(f,C)的声明集不同,则合并是不明确的:新的S(f,C)是具有无效声明集的查找集和子对象集.在后续合并中,无效的声明集被认为与其他任何不同.
- 否则,新的S(f,C)是具有共享声明集和子对象集的并集的查找集.