我正在尝试了解以下类模板的工作原理(取自here),但我无法理解它:
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)));
};
更具体地说,我不理解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;
}
输出(ideone):
false
true
true
答案 0 :(得分:8)
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),由两个组件集组成:< em>声明集,一组名为f
的成员;和子对象集,这是一组子对象,其中找到了这些成员的声明(可能包括 using-declaration )。在声明集中, using-declaration 被它们指定的成员替换,类型声明(包括注入类名称)被它们指定的类型替换。 S(f,C)的计算方法如下:4如果
C
包含名称f
的声明,则声明集包含f
中声明的C
声明,满足语言结构的要求。查找发生的位置。 [注意:在 elaborated-type-specifier 或 base-specifier 中查找名称,例如,忽略所有非类型声明,查找嵌套名称说明符中的名称时忽略函数,变量和枚举器声明。另一个例子是,在 using-declaration 中查找名称包括一个类或枚举的声明,该声明通常会被同一范围内该名称的另一个声明隐藏。 - 结束注释]如果结果声明集不为空,则子对象集本身包含C
,计算完成。5否则(即
C
不包含f
的声明或结果声明集为空), S(f,C)最初为空。 如果C
有基类,请在每个直接基类子对象 B i 中计算f
的查找集,并合并每个查找集 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)是一个包含共享声明集和子对象集合的查找集。
答案 1 :(得分:2)
Base
可以从operator()
或Type
继承BaseMixin
,如果Type
有operator()
则隐藏BaseMixin
'运营商。
因此,如果Type
未定义operator ()
,则Base
的{{1}}可以隐式投放到operator()
的{{1}}。然后,BaseMixin
与operator()
匹配。
相反,如果deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0)
定义了U==Base
,则Type
的{{1}}无法投放到operator()
的{{1}},Base
不会匹配。