Template member function cast bug in VC80SP1 (and more...)
Hi to all,
writing a small piece of metaprogramming code, we had to face some problems:
one at least seems a bug in VC8.0 (SP1).
here's an overview: we have
class VECTOR
class MAP_BASE : private VECTOR
class MAP : public MAP_BASE
MAP_BASE makes public some (originally public) things in VECTOR, such as
VECTOR::erase, and both MAP_BASE and MAP define 'iterator' as
VECTOR::iterator.
we'll omit template parameters from the text, a precise c++ sample is
appended below.
(problem 1: less important)
if we write
typedef MAP::iterator (MAP::*pointer_to_erase_t)(MAP::iterator);
pointer_to_erase_t p = &MAP::erase
every compiler we tried (vc8, gcc4, intel10) says that 'erase' is
inaccessible (in a private base), despite the fact that the member is...
public, due to the using declaration.
actually we had a quick look in the standard, and surprisingly, it just says
'if the member is in a private base, the conversion (of the pointer) fails.
is this an omission?
(problem 2: very important)
is there some way to test if a class has ANY member/member function
named -let's say- swap?
we just need to know if the expression "&T::swap" is legal or not.
first, we tried the following but it seems that it's illegal:
template <typename S>
class has_something_named_swap
{
class yes { char dummy[2]; };
typedef char no;
template <typename T>
static yes test(T*, size_t = sizeof(&T::swap));
static no test(void*, size_t = 0);
public:
static const bool value = sizeof(test( (S*)0 ))!= sizeof(no);
};
the following variant works well in gcc4 and intel10 (and according to the
SFINAE principle, it should work!!!)
but does not compile in vc8 (it says something like: overloaded function,
illegal sizeof operand):
template <size_t N> class yes { char dummy[2]; };
template <typename T>
static yes<sizeof(&T::swap)> test(T*);
I personally think it's an artificial limitation in VC.
(problem 3: very important)
it looks like VC8 is unable to resolve a
pointer-to-overloaded-member-function when some of the overloads are in
VECTOR and some are in MAP_BASE.
Here's a minimal sample that shows the problem: both intel10 and gcc4 do
compile it correctly.
(NOTE: DON'T RUN - IT WILL CRASH - BUT IT SHOULD COMPILE)
the most satisfactory workaround we found is to duplicate VECTOR::erase in
MAP_BASE (it's commented in the code), but as a rule this is not nice (if
also std::map had the same problem, we would have had to overload erase_gap
on std::map, which is exactly what we are trying to avoid...)
#include <map>
#include <vector>
template <typename T>
class VECTOR
{
typedef std::vector<T> vector_t;
vector_t data_;
public:
typedef typename vector_t::iterator iterator;
iterator begin() { return data_.begin(); }
iterator erase(iterator pos)
{
return (pos);
}
};
template <typename key_t, typename mapped_t>
class MAP_BASE : private VECTOR< std::pair<key_t, mapped_t> >
{
public:
typedef VECTOR< std::pair<key_t, mapped_t> > map_t;
typedef typename map_t::iterator iterator;
using map_t::begin;
using map_t::erase;
size_t erase(const key_t& x)
{
return 0;
}
// iterator erase(iterator pos)
// {
// return map_t::erase(pos);
// }
};
template <typename key_t, typename mapped_t>
class MAP : public MAP_BASE<key_t, mapped_t>
{
typedef MAP_BASE<key_t, mapped_t> base_t;
public:
using base_t::iterator;
};
template <typename container_t, typename iterator_t, typename base_t>
inline void erase_gap2(container_t& c, iterator_t& i, iterator_t
(base_t::*)(iterator_t))
{
i = c.erase(i);
}
template <typename container_t, typename iterator_t, typename base_t>
inline void erase_gap2(container_t& c, iterator_t& i, void
(base_t::*)(iterator_t))
{
c.erase(i++);
}
template <typename container_t>
typename container_t::iterator erase_gap(container_t& c, typename
container_t::iterator i)
{
erase_gap2(c, i, &container_t::erase);
return i;
}
int main()
{
std::vector<double> v;
std::map<double, double> m;
MAP<double, double> w;
erase_gap(w, w.begin());
erase_gap(v, v.begin());
erase_gap(m, m.begin());
return 0;
}