3

I've been hanging around a problem for at least two weeks, being blocked by stuff that I'm not able to understand and making questions in SO that doesn't really pointed to the real problem (silly me!). Finally now, I wish I had found the real point of my headache in this question.

I've been using a template struct as helper to detect whether or not a given type has a member method or not, this template struct looks like this:

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

The main idea arround the struct W is to put as fourth parameter a member function pointer to the member function I need to test using the SFINAE trick, in a previous question an alternative was suggested and it helped me so much to understand many concepts that I've been missing, but in the end, it doesn't solves my real problem.

The piece of code that i'm working on, must work in both Linux and Windows platforms, the compiler i'm using for windows is MSVC (Visual Stuido 2010 10.0) and gcc (Devian 4.4.5-8) on Linux side. The problem is that the same piece of code doesn't compile in MSVC but it does in gcc (I was shocked, usually is the oposite, cause MSVC is less standard-strict).

The origin of the problem was that my SFINAE approach fails to detect the method set::insert while compiling under MSVC, in order to look for this fail i wrote a 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;
}

As i mentioned before: this sample compiles without problem using gcc, but while using MSVC gives an error at the declaration of 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

While creating a function pointer works as expected, so the declaration of p_1 compiles; but with the same signature as template parameter does not. This is why the set::insert detection fails during the symbols substitution of SFINAE. The error text cannot convert from 'overloaded-function' doesn't provides me any clue about what's going on (or i'm not able to find any, due my poor english understanding).

At first sight, i've been thinking that the problem is the symbols substitution, but it would make sense if it failed in both MSVC and gcc as well. So for now i'm wondering if the problem is some MSVC compiler-specific way to do things.

Any clue about why is it failing under MSVC and how to make gcc and MSVC works in the same way?

Extra question: std::map and std::set provides a nested type _Pairib as return type for ::insert method under the MSVC implementation of red-black tree (or whatever is under map and set), there's no equivalent in the gcc implementation?

EDIT:

After reading the chill answer, I've looked into the MSVC's std::set implementation.

std::set is a derived class of std::_Tree that provides the method i want to check:

// 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));
    }

This insert method belongs to the std::_Tree scope, not to std::set scope, but it is into the public scope and is a inherited method as well, so why it isn't accesible during the name substitution?

Community
  • 1
  • 1
PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
  • 1
    Regarding the extra question: the name `_Pairib` is reserved for the implementation, user code should never refer to it. The set of reserved identifiers are: identifies containing double underscore (`__`), or beginning with single underscore and a capital letter (`_P..`) in any namespace or single underscore followed by any character in the global namespace. – David Rodríguez - dribeas Nov 26 '12 at 13:10

2 Answers2

2

The cause for failing is that in the VC stdlib, the insert is not actually a member of the set class template itself, but is inherited:

Test case,

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. The error is error in both some GCC and some MSVC.

But it is on the public scope and is an inherited method as well, so I don't get why isn't it accesible.

Because no conversion is allowed in this case, "14.3.2. Template non-type arguments [#5]"

For a non-type template-parameter of type pointer to member function, if the template-argument is of type std::nullptr_t, the null member pointer conversion (4.11) is applied; otherwise, no conversions apply. If the template-argument represents a set of overloaded member functions, the matching member function is selected from the set (13.4).

PPS. So, you do the conversion explicitly and voila, you don't need to care for possible base classes:

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;
chill
  • 16,470
  • 2
  • 40
  • 44
  • I thought that the problem isn't related to inheritance... I'll check it. – PaperBirdMaster Nov 26 '12 at 15:29
  • The declaration & implementation of `_Pairib insert(const value_type&)` is in the `class _Tree` not in the `class set`, you're right. But it is on the public scope and is an inherited method as well, so I don't get why isn't it accesible. – PaperBirdMaster Nov 27 '12 at 07:55
  • I've solved this problem with a workaround many months away, this answer doesn't solves my specific problem but is really worth to read because it could point someone with the same problem in the way of a solution. – PaperBirdMaster Jun 28 '13 at 12:00
0

According to this question, the Standard does not define that you can take member function pointers to members of Standard objects.

And _Pairib is an MSVC implementation detail- naturally you won't find a similar thing in GCC.

Community
  • 1
  • 1
Puppy
  • 144,682
  • 38
  • 256
  • 465