Re: Overloading a template member function with a dependent name
* mlimber, on 20.05.2011 23:21:
On May 20, 5:21 pm, "Alf P. Steinbach /Usenet"<alf.p.steinbach
+use...@gmail.com> wrote:
* mlimber, on 20.05.2011 22:37:
I'm trying to overload a template member function with a dependent
name involved. The following does not work as I'd like:
class C
{
public:
template<class Iter>
void Foo( Iter )
{
std::cout<< "Normal\n";
}
template<class T, std::size_t N>
void Foo( typename std::tr1::array<T,N>::iterator )
{
std::cout<< "Special\n";
}
};
int main()
{
C c;
std::tr1::array<int,10> a1;
c.Foo( a1.begin() ); // Doesn't print "Special"!
}
How can I get that last line in main() to invoke the special C::Foo()?
Why there is a problem:
<code>
template< class Type>
struct Foo
{
typedef int T;
};
template< class Type>
void foo( typename Type::T ) {}
int main()
{
foo( 666 ); // !Nope
}
</code>
This does not compile because the compiler cannot deduce the template parameter.
It cannot because it would be an unbounded reverse lookup: given the actual
argument type `int`, find the type `Type` that has a member typedef of `T` with
type `int`. There can be zillions or none of such types.
There are techniques that sometimes can be used to still do something /like/ you
seem to be aiming for.
One crucial question for applicability of such techniques here is N: do you need
to know it? In that case I don't know any solution other than changing the
design. I don't think it can be inferred from the iterator type, at all.
Thanks, Alf. I don't need N (it would be sugar to have it, but since
I'm actually passing in begin and end, I can calculate it). So do
tell!
Uh, I don't have a suitable implementation to test with, since the g++ 4.4.1
tr1/array has just raw pointers as iterators. Not much can be deduced from a raw
pointer type! But as an example of some of the ideas,
<code>
#include <iterator>
#include <iostream>
#include <tr1/array>
#include <vector>
#ifdef FOR_REAL
template< class Type, int n >
struct MyArray
: std::tr1::array< Type, n >
{};
#else
template< class Type, int n >
struct MyArray
{
Type data;
typedef std::iterator< std::random_access_iterator_tag, Type >
iterator;
iterator begin() { return iterator(); }
Type& operator[]( int i ) { return data; }
};
#endif
template< class A, class B > struct IsSameType{ enum { yes = false }; };
template< class Type > struct IsSameType< Type, Type >{ enum { yes = true }; };
#define STATIC_ASSERT( e ) typedef char shouldBeTrue[(e)?1:-1]
STATIC_ASSERT(
!(IsSameType<
std::vector<int>::iterator,
MyArray<int, 10>::iterator
>::yes)
);
STATIC_ASSERT(
!(IsSameType<
int*,
MyArray<int, 10>::iterator
>::yes)
);
namespace detail {
struct AGeneralIter {};
struct ASpecialIter {};
template< class Iter, class Pointee >
struct IteratorCategory
{
typedef AGeneralIter T;
};
// These can possibly be generated by the Boost macro repeat support.
template< class Pointee >
struct IteratorCategory< typename MyArray< Pointee, 10 >::iterator,
Pointee >
{
typedef ASpecialIter T;
};
} // namespace detail
class C
{
private:
template< class IterCategory, class Iter > struct Impl;
template< class Iter >
struct Impl< detail::AGeneralIter, Iter >
{
static void foo( Iter )
{
std::cout << "Normal\n";
}
};
template< class Iter >
struct Impl< detail::ASpecialIter, Iter >
{
static void foo( Iter )
{
std::cout << "Special\n";
}
};
public:
template< class Iter >
void foo( Iter it )
{
typedef typename std::iterator_traits< Iter >::value_type
Pointee;
typedef typename detail::IteratorCategory< Iter, Pointee >::T
Category;
Impl< Category, Iter >::foo( it );
}
};
int main()
{
C c;
MyArray< int, 10 > a1;
c.foo( &a1[0] ); // If the code compiles, prints "Normal".
c.foo( a1.begin() ); // If the code compiles, prints "Special".
}
</code>
Simply define FOR_REAL to check how it fares for your tr1/array implementation.
Or, at least I hope that that's possible; naturally I haven't been able to test
that! ;-)
Cheers, & sorry but on 2nd thought I don't think this helps with imm. prob.,
- Alf
--
blog at <url: http://alfps.wordpress.com>