【问题标题】:MSVC pointer to overloaded function in template parameterMSVC 指向模板参数中重载函数的指针
【发布时间】: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::mapstd::set 提供了一个嵌套类型 _Pairib 作为红黑树的 MSVC 实现下的 ::insert 方法的返回类型(或 mapset 下的任何东西) , gcc 实现中没有等价物吗?

编辑:

阅读chill answer 后,我研究了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作用域,但它属于公共作用域,也是继承方法,所以为什么在名称替换过程中无法访问?

【问题讨论】:

  • 关于额外的问题:名称_Pairib 是为实现保留的,用户代码不应该引用它。保留标识符的集合是:标识包含双下划线 (__),或在任何命名空间中以单下划线和大写字母 (_P..) 开头,或在全局命名空间中后跟单个下划线的任何字符。

标签: c++ templates visual-c++ gcc sfinae


【解决方案1】:

失败的原因是在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)。

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

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;

【讨论】:

  • 我认为问题与继承无关...我会检查它。
  • _Pairib insert(const value_type&amp;)的声明和实现是在class _Tree而不是class set,你是对的。但它在公共范围内,也是一个继承的方法,所以我不明白为什么它不能访问。
  • 我已经在几个月后通过一种解决方法解决了这个问题,这个答案并没有解决我的具体问题,但真的值得一读,因为它可以指出有同样问题的人解决方案。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-30
    • 1970-01-01
    • 1970-01-01
    • 2014-04-14
    • 2015-04-26
    • 1970-01-01
    相关资源
    最近更新 更多