【发布时间】:2012-11-26 10:16:10
【问题描述】:
我已经在一个问题上徘徊了至少两个星期,被我无法理解的东西所阻碍,并且在 SO 中提出的问题并没有真正指出真正的问题(我真傻!)。最后,我希望我能在这个问题中找到我头痛的真正点。
我一直使用模板结构作为帮助器来检测给定类型是否具有成员方法,这个模板结构看起来像这样:
template
<
typename Type,
typename Return,
typename Parameter,
Return (Type::*)(Parameter)
> struct W {};
围绕struct W 的主要思想是将成员函数指针作为第四个参数,该指针指向我需要使用 SFINAE 技巧测试的成员函数,in a previous question 提出了一个替代方案,它帮助我理解了许多我一直缺少的概念,但最终,它并没有解决我真正的问题。
我正在处理的代码必须在 Linux 和 Windows 平台上都可以工作,我用于 Windows 的编译器是 MSVC (Visual Stuido 2010 10.0) 和 gcc (Devian 4.4.5-8) Linux 端。问题是同一段代码不能在 MSVC 中编译,但在 gcc 中可以编译(我很震惊,通常是相反的,因为 MSVC 的标准不那么严格)。
问题的根源是我的 SFINAE 方法在 MSVC 下编译时未能检测到方法 set::insert,为了查找此失败我写了一个 simple test:
#include <set>
int main(int argc, char **argv)
{
typedef std::set<int> setint;
std::pair<setint::iterator, bool> (setint::*p_1)(const setint::value_type &) = &setint::insert;
W<setint, std::pair<setint::iterator, bool>, const setint::value_type &, &setint::insert> w_1;
return 0;
}
正如我之前提到的:这个示例使用 gcc 编译没有问题,但是在使用 MSVC 时,w_1 的声明会出错:
error C2440: 'specialization' : cannot convert from 'overloaded-function' to 'std::pair<_Ty1,_Ty2> (__thiscall std::set<_Kty>::* )(const int &)'
with
[
_Ty1=std::_Tree_const_iterator<std::_Tree_val<std::_Tset_traits<int,std::less<int>,std::allocator<int>,false>>>,
_Ty2=bool,
_Kty=int
]
None of the functions with this name in scope match the target type
虽然创建函数指针按预期工作,所以 p_1 的声明编译;但具有与模板参数相同的签名没有。这就是为什么在 SFINAE 的符号替换期间set::insert 检测失败的原因。错误文本 cannot convert from 'overloaded-function' 没有为我提供任何关于发生了什么的线索(或者我无法找到任何线索,因为我的英语理解不佳)。
乍一看,我一直认为问题出在符号替换上,但如果它在 MSVC 和 gcc 中都失败了,这将是有意义的。所以现在我想知道问题是否出在一些特定于 MSVC 编译器的方式上。
任何关于它为什么在 MSVC 下失败以及如何使 gcc 和 MSVC 以相同方式工作的线索?
额外的问题:std::map 和 std::set 提供了一个嵌套类型 _Pairib 作为红黑树的 MSVC 实现下的 ::insert 方法的返回类型(或 map 和 set 下的任何东西) , gcc 实现中没有等价物吗?
编辑:
阅读chill answer 后,我研究了MSVC 的std::set 实现。
std::set 是std::_Tree 的派生类,它提供了我要检查的方法:
// class std::_Tree, file xtree in the path 'VisualStudioPath/VC/include/'
_Pairib insert(const value_type& _Val)
{ // try to insert node with value _Val, favoring right side
return (insert(_Val, false));
}
这个insert方法属于std::_Tree作用域,不属于std::set作用域,但它属于公共作用域,也是继承方法,所以为什么在名称替换过程中无法访问?
【问题讨论】:
-
关于额外的问题:名称
_Pairib是为实现保留的,用户代码不应该引用它。保留标识符的集合是:标识包含双下划线 (__),或在任何命名空间中以单下划线和大写字母 (_P..) 开头,或在全局命名空间中后跟单个下划线的任何字符。
标签: c++ templates visual-c++ gcc sfinae