Overloading a template member function with a dependent name


M

mlimber

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()?
 
Ad

Advertisements

M

mlimber

   template<class T, std::size_t N>
   void Foo( typename std::tr1::array<T,N>::iterator )
[snip]

One additional note: I can't use T* as a work-around here because in
VS2010's implementation, std::tr1::array<T,N>::iterator is the
internal type "_Array_iterator<_Ty, _Size>", which is a checked
iterator (at least in debug mode).

Cheers! --M
 
M

mlimber

* mlimber, on 20.05.2011 22:37:





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!

Cheers! --M
 
A

Alf P. Steinbach /Usenet

* 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.


Cheers & hth.,

- Alf "hungry"
 
A

Alf P. Steinbach /Usenet

* mlimber, on 20.05.2011 23:21:
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 said:
);
STATIC_ASSERT(
!(IsSameType<
int*,
MyArray said:
);

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
 
R

Ruben Safir

mlimber said:
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()?

Foo is not in your class, and I'm actually surprise this compiles with
the keyword typename in the parameter list.

Ruben
 
Ad

Advertisements

A

Alf P. Steinbach /Usenet

* Ruben Safir, on 21.05.2011 01:33:
Foo is not in your class, and I'm actually surprise this compiles with
the keyword typename in the parameter list.

Foo is a member of class C which is user defined. The keyword `typename` is
needed in the parameter list. It should not compile without it.

Cheers & hth.,

- Alf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top