Boost logo

Boost :

Subject: [boost] help detecting presence of operator[]
From: Jeffrey Hellrung (jhellrung_at_[hidden])
Date: 2010-01-22 15:52:52


Hi all,

I'm trying to perform a compile-time check to determine if a class has
an operator[]. I'm using a "trick" I think I've seen in Adobe's library
and on this list in the past:

-------- BEGIN CODE --------
template< class T >
struct has_op_bracket
{
     struct base_t { void operator[]( int ); };
     struct derived_t : T, base_t { };
     template< void (base_t::*)( int ) > struct sfinae { typedef char
type; };
     template< class U > static typename sfinae< &U::operator[] >::type
is_ambiguous(int);
     template< class U > static int is_ambiguous(...);
     static const bool value = sizeof( is_ambiguous< derived_t >(0) ) != 1;
};
-------- END CODE --------

The way I understand this working is if T has no operator[], then
&derived_t::operator[] unambiguously refers to *base_t::operator[], and
so the first is_ambiguous overload is selected. Otherwise,
&derived_t::operator[] is ambiguous, which is sufficient (I guess...?)
for SFINAE to kick in, and the second is_ambiguous overload is selected.

Now given the following definitions of X0 and X1:

-------- BEGIN CODE --------
struct X0 { };

struct X1 { void operator[](void*); };
-------- END CODE --------

then the following static assertions pass, as expected:

-------- BEGIN CODE --------
BOOST_STATIC_ASSERT(( !has_op_bracket<X0>::value ));
BOOST_STATIC_ASSERT(( has_op_bracket<X1>::value ));
-------- END CODE --------

However, if X2 is defined as

-------- BEGIN CODE --------
struct X2
{
     template< class T > struct R : T { };
     template< class T >
     typename R<T>::type operator[](T);
};
-------- END CODE --------

then has_op_bracket<X2>::value fails to compile on MSVC9, but (according
to Dinkum Exam[1]) it does compile just fine on "EDG/C++". The error in
MSVC9 is

-------- BEGIN OUTPUT --------
error C2516: 'T' : is not a legal base class
1> d:\test\main.cpp(9) : see declaration of 'T'
1> d:\test\main.cpp(9) : see reference to class template
instantiation 'X2::R<T>' being compiled
1> with
1> [
1> T=int
1> ]
1> d:\test\main.cpp(21) : see reference to class template
instantiation 'has_op_bracket<T>' being compiled
1> with
1> [
1> T=X2
1> ]
-------- END OUTPUT --------

generated by the following program:

-------- BEGIN CODE --------
template< class T >
struct has_op_bracket
{
     struct base_t { void operator[]( int ) const; };
     struct derived_t : T, base_t { };
     template< void (base_t::*)( int ) const > struct sfinae { typedef
char type; };
     template< class U > static typename sfinae< &U::operator[] >::type
is_ambiguous(int);
     template< class U > static int is_ambiguous(...);
     static const bool value = sizeof( is_ambiguous< derived_t >(0) ) !=
1; // line 9
};

struct X2
{
     template< class T > struct R : T { };
     template< class T >
     typename R<T>::type operator[](T);
};

int main()
{
     has_op_bracket<X2>::value; // line 21
     return 0;
}
-------- END CODE --------

What I don't understand is why MSVC is trying to instantiate R<int>. It
seems to be grabbing the int from the parameter type of the member
function pointer template parameter in sfinae, which doesn't seem like
the right thing to do. I can change that parameter to anything else (as
long as it doesn't have a nested type typedef) and the error persists.

Is MSVC correct, or is this a compiler bug? Either way, is there an
alternative, more portable approach to achieve the desired effect?

Thanks in advance,

- Jeff

[1] http://www.dinkumware.com/exam/default.aspx


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk