3

我已经在一个问题上徘徊了至少两个星期,被我无法理解的东西所阻碍,并且在 SO 中提出的问题并没有真正指出真正的问题(我真傻!)。最后,我希望我能在这个问题中找到我头痛的真正点。

我一直在使用模板结构作为帮助器来检测给定类型是否具有成员方法,这个模板结构如下所示:

template
<
    typename Type,
    typename Return,
    typename Parameter,
    Return (Type::*)(Parameter)
> struct W {};

围绕的主要思想struct W是将成员函数指针作为第四个参数,该指针指向我需要使用 SFINAE 技巧测试的成员函数,在上一个问题中提出了一个替代方案,它帮助我理解了我已经了解的许多概念一直失踪,但最终,它并没有解决我真正的问题。

我正在处理的代码必须在 Linux 和 Windows 平台上都可以工作,我用于 Windows 的编译器是 Linux 端的 MSVC(Visual Stuido 2010 10.0)和 gcc(Devian 4.4.5-8)。问题是同一段代码不能在 MSVC 中编译,但在 gcc 中可以编译(我很震惊,通常是相反的,因为 MSVC 的标准不那么严格)。

问题的根源是我的 SFINAE 方法set::insert在 MSVC 下编译时无法检测到该方法,为了查找此失败,我编写了一个简单的测试

#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编译声明;但与模板参数具有相同的签名没有。这就是set::insert在 SFINAE 的符号替换期间检测失败的原因。错误文本cannot convert from 'overloaded-function'没有为我提供任何关于正在发生的事情的线索(或者由于我的英语理解不佳,我无法找到任何线索)。

乍一看,我一直认为问题出在符号替换上,但如果它在 MSVC 和 gcc 中都失败了,这将是有意义的。所以现在我想知道问题是否是一些特定于 MSVC 编译器的做事方式。

关于为什么它在 MSVC 下失败以及如何使 gcc 和 MSVC 以相同方式工作的任何线索?

额外的问题:std::mapandstd::set提供了一个嵌套类型作为红黑树的 MSVC 实现下的方法的_Pairib返回类型(或任何在and下的方法),在 gcc 实现中没有等价物吗?::insertmapset

编辑:

在阅读了寒冷的答案后,我研究了 MSVC 的std::set实现。

std::setstd::_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作用域,但属于公共作用域,也是继承的方法,为什么在名称替换时不能访问呢?

4

2 回答 2

2

失败的原因是在VC标准库中,insert实际上并不是set类模板本身的成员,而是被继承的:

测试用例,

template
<
    typename Type,
    typename Return,
    typename Parameter,
    Return (Type::*)(Parameter)
> struct W {};

struct A { void foo (int); };

struct B : public A {};

int main(int argc, char **argv)
{
    void (B::*p)(int) = &B::foo;
    // W<B, void, int, &B::foo> w; // error
    W<A, void, int, &A::foo> w;

    return 0;
}

PS。该错误在某些 GCC 和某些 MSVC 中都是错误的。

但它在公共范围内,也是一个继承的方法,所以我不明白为什么它不能访问。

因为在这种情况下不允许转换,所以“14.3.2. 模板非类型参数 [#5]”

对于指向成员函数的类型指针的非类型模板参数,如果模板参数的类型为 std::nullptr_t,则应用空成员指针转换 (4.11);否则,不适用任何转换。如果模板参数表示一组重载的成员函数,则从集合中选择匹配的成员函数(13.4)。

聚苯乙烯。因此,您明确地进行转换,瞧,您不需要关心可能的基类:

W<setint,
  std::pair<setint::iterator, bool>,
  const setint::value_type &,
  static_cast<std::pair<setint::iterator, bool> (setint::*)(const setint::value_type &)> (&setint::insert)> w_1;
于 2012-11-26T10:58:30.223 回答
0

根据这个问题,标准没有定义您可以将成员函数指针指向标准对象的成员。

并且_Pairib是一个 MSVC 实现细节 - 自然你不会在 GCC 中找到类似的东西。

于 2012-11-26T10:19:51.777 回答