469,570 Members | 1,709 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,570 developers. It's quick & easy.

Iterators and ints get confused in template parameter

I'm writing an STL-style data container, and this problem is puzzling
me. The following code should demonstrate the problem:

//----------------------------------------------------------
#include <iostream>
#include <list>

template<typename T>
class MyClass
{
public:
void assign(size_t amount, const T& value)
{
std::cout << amount << " " << value << std::endl;
}

template<typename InputIterator>
void assign(InputIterator first, InputIterator last)
{
while(first != last)
{
std::cout << *first << std::endl;
++first;
}
}
};

int main()
{
std::list<intl1, l2;
l1.assign(10, 5); // ok
l2.assign(l1.begin(), l1.end()); // ok

MyClass<intmc;
mc.assign(l1.begin(), l1.end()); // ok
mc.assign(10, 5); // error, wrong function gets called
}
//----------------------------------------------------------

I'm using gcc 4.1.2.

I understand *why* the problem is happening. However, I don't
understand why it's not happening with std::list, and I have no idea how
to get around the problem (in the same way as std::list does).

I have looked at the std::list source code in the gcc C++ libraries,
and I don't see any fancy trick being used to differentiate between the
two assign() functions. Yet it just works with std::list, but it doesn't
work with my code.

Any pointers?
Jul 20 '08 #1
11 1229
Juha Nieminen wrote:
I'm writing an STL-style data container, and this problem is puzzling
me. The following code should demonstrate the problem:

//----------------------------------------------------------
#include <iostream>
#include <list>

template<typename T>
class MyClass
{
public:
void assign(size_t amount, const T& value)
{
std::cout << amount << " " << value << std::endl;
}

template<typename InputIterator>
void assign(InputIterator first, InputIterator last)
{
while(first != last)
{
std::cout << *first << std::endl;
++first;
}
}
};

int main()
{
std::list<intl1, l2;
l1.assign(10, 5); // ok
l2.assign(l1.begin(), l1.end()); // ok

MyClass<intmc;
mc.assign(l1.begin(), l1.end()); // ok
mc.assign(10, 5); // error, wrong function gets called
Both '10' and '5' have the same type, 'int'. You have two functions
'assign', which are essentially overloaded. One takes two arguments,
'size_t' and 'int' (the 'T', which is 'int' for 'mc' object), the
other takes two arguments, both 'InputIterator', whichever that
might be. Which is the better match? I am guessing that you already
"know" all this since you say that you "understand *why" the problem
is happening". I spelled it out for the benefits of the others, then.
}
//----------------------------------------------------------

I'm using gcc 4.1.2.

I understand *why* the problem is happening. However, I don't
understand why it's not happening with std::list, and I have no idea
how to get around the problem (in the same way as std::list does).

I have looked at the std::list source code in the gcc C++ libraries,
and I don't see any fancy trick being used to differentiate between
the two assign() functions. Yet it just works with std::list, but it
doesn't work with my code.

Any pointers?
You could limit the range of types accepted by the 'assign' member
template by giving it a second template argument, for example, which
would become an ivalid type if the type of the argument is not
an iterator (SFINAE). It's Sunday and my brain isn't working well,
so I can't really suggest any changes to the code... Sorry.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jul 20 '08 #2
On Jul 20, 11:56 am, Juha Nieminen <nos...@thanks.invalidwrote:
I'm writing an STL-style data container, and this problem is puzzling
me. The following code should demonstrate the problem:

//----------------------------------------------------------
#include <iostream>
#include <list>

template<typename T>
class MyClass
{
public:
void assign(size_t amount, const T& value)
{
std::cout << amount << " " << value << std::endl;
}

template<typename InputIterator>
void assign(InputIterator first, InputIterator last)
{
while(first != last)
{
std::cout << *first << std::endl;
++first;
}
}

};

int main()
{
std::list<intl1, l2;
l1.assign(10, 5); // ok
l2.assign(l1.begin(), l1.end()); // ok

MyClass<intmc;
mc.assign(l1.begin(), l1.end()); // ok
mc.assign(10, 5); // error, wrong function gets called}

//----------------------------------------------------------

I'm using gcc 4.1.2.

I understand *why* the problem is happening. However, I don't
understand why it's not happening with std::list, and I have no idea how
to get around the problem (in the same way as std::list does).

I have looked at the std::list source code in the gcc C++ libraries,
and I don't see any fancy trick being used to differentiate between the
two assign() functions. Yet it just works with std::list, but it doesn't
work with my code.

Any pointers?
Which function gets called?

You don't have a member function with this signature assign(int,
int ); While int is guaranteed to convert to size_t, there is no such
guarantees available for a object of class T. One solution is add
this member function to this class, another is add user-defined
conversion operator.

PuzzleCracker
Jul 20 '08 #3
Juha Nieminen wrote:
I have looked at the std::list source code in the gcc C++ libraries,
and I don't see any fancy trick being used to differentiate between the
two assign() functions.
Actually I was wrong. The assign() functions themselves don't use any
trick, but the internal functions they call seem to use some kind of
obscure template magic to differentiate between the two cases.

The code in question is filled with names starting with _ and __, for
example:

typedef typename std::__is_integer<_InputIterator>::__type _Integral;

This seems to be telling me that this is not standard library code,
but code specific to gcc (or whatever STL library it's using). Thus it
wouldn't probably be very portable to try to copy this implementation
verbatim.

Any suggestions how I could solve this problem with standard STL code
only, and preferably without having to reimplement enormous libraries?
(Also I wouldn't want to make specializations for assign() for each
possible integral type.)
Jul 20 '08 #4
On Jul 20, 7:08 pm, Juha Nieminen <nos...@thanks.invalidwrote:
Juha Nieminen wrote:
I have looked at the std::list source code in the gcc C++
libraries, and I don't see any fancy trick being used to
differentiate between the two assign() functions.
Actually I was wrong. The assign() functions themselves don't
use any trick, but the internal functions they call seem to
use some kind of obscure template magic to differentiate
between the two cases.
The code in question is filled with names starting with _ and
__, for example:
typedef typename std::__is_integer<_InputIterator>::__type _Integral;
This seems to be telling me that this is not standard library code,
Why? It's part of the implementation, so it's bound to use
funny names. But it's part of the implementation of the
standard library.
but code specific to gcc (or whatever STL library it's using).
Thus it wouldn't probably be very portable to try to copy this
implementation verbatim.
Maybe, maybe not. It's possible that the g++ library uses some
special g++ extensions here, but somehow I doubt it. And it
certainly can be done in standard C++. (The standard requires
it be done somehow, of course.)
Any suggestions how I could solve this problem with standard
STL code only, and preferably without having to reimplement
enormous libraries? (Also I wouldn't want to make
specializations for assign() for each possible integral type.)
You only have to specialize some discriminator template for each
possible integral type. That descrimator type has a typedef,
you call a function using an argument based on that typedef, and
operator overloading does the rest. Something along the lines
(untested):

class True {} ;
class False {} ;

template< typename T >
struct IntegerDiscriminator
{
typedef False Type ;
} ;

template<>
struct IntegerDiscriminator< int >
{
typdef True Type ;
}

// ...

And then:

template< typename T >
class MyClass
{
public:
void assign( size_t count, T const& value ) ;
template< typename ForwardIterator >
void assign( ForwardIterator begin,
ForwardIterator end )
{
doAssign( begin, end, IntegerDiscriminator< T
>::type() ) ;
}

private:
void doAssign( size_t count, T value, True )
{
assign( count, value ) ;
}
template< typename ForwardIterator >
void doAssign( ForwardIterator begin,
ForwardIterator end,
False )
{
while ( begin != end ) ...
}
} ;

Or something like that. For all the details, see the
Vandevoorde and Josuttis.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 20 '08 #5
James Kanze wrote:
>The code in question is filled with names starting with _ and
__, for example:
>typedef typename std::__is_integer<_InputIterator>::__type _Integral;
>This seems to be telling me that this is not standard library code,

Why?
Does the C++ standard really specify templates like std::__is_integer?

If not, then the code above is specific to the library in question and
thus not portable all in itself.
It's part of the implementation, so it's bound to use
funny names. But it's part of the implementation of the
standard library.
My point is: I want to replicate the functionality, but I can't just
copy it verbatim from gcc's library because it uses non-standard types
like the ones in the above example line.
>but code specific to gcc (or whatever STL library it's using).
Thus it wouldn't probably be very portable to try to copy this
implementation verbatim.

Maybe, maybe not.
Unless the C++ standard specifies things like std::__is_integer, the
answer is no.

The only thing I could do is to copy the functionality of that type
(and whatever it might use) into my code, but I was wondering if there
isn't any easier way.
It's possible that the g++ library uses some
special g++ extensions here, but somehow I doubt it. And it
certainly can be done in standard C++. (The standard requires
it be done somehow, of course.)
I don't think you really understood what I meant.
template<>
struct IntegerDiscriminator< int >
{
typdef True Type ;
}
Doesn't that only take care of the case where the parameter is of type
'int'? What about unsigned int, long, unsigned long, short, unsigned
short, char, signed char, unsigned char, long long, unsigned long long...
Or something like that. For all the details, see the
Vandevoorde and Josuttis.
I didn't understand that.
Jul 20 '08 #6
On Jul 21, 1:13 am, Juha Nieminen <nos...@thanks.invalidwrote:
James Kanze wrote:
The code in question is filled with names starting with _ and
__, for example:
typedef typename std::__is_integer<_InputIterator>::__type _Integral;
This seems to be telling me that this is not standard library code,
Why?
Does the C++ standard really specify templates like std::__is_integer?
The C++ standard does not specify how any of the standard
containers are implemented. It does require the implementation
to discriminate between integral types and other types in the
two iterator template member functions.
If not, then the code above is specific to the library in
question and thus not portable all in itself.
Obviously, such code is specific to the library. That doesn't
mean that it uses compiler extensions, and isn't "portable"
(except that user code doesn't have the right to use names
beginning with __). It's quite possible to write something like
__is_integer in portable C++, and I'd be fairly surprised if the
implementation in the g++ library wasn't in portable C++ (again,
modulo the fact that the names aren't legal in user code).
It's part of the implementation, so it's bound to use funny
names. But it's part of the implementation of the standard
library.
My point is: I want to replicate the functionality, but I
can't just copy it verbatim from gcc's library because it uses
non-standard types like the ones in the above example line.
And my point is: I don't see your point. Why can't you copy
__is_integer, changing the names, and use it?
but code specific to gcc (or whatever STL library it's using).
Thus it wouldn't probably be very portable to try to copy this
implementation verbatim.
Maybe, maybe not.
Unless the C++ standard specifies things like
std::__is_integer, the answer is no.
Why? I'm pretty sure that the answer is yes.
The only thing I could do is to copy the functionality of that
type (and whatever it might use) into my code, but I was
wondering if there isn't any easier way.
Well, rather than copying, the actual code isn't too difficult
to understand, so it might be worth implementing it yourself,
for pedagogical reasons. Otherwise, there are implementations
of it in Boost, and doubtlessly elsewhere as well; the Boost
implementation is part of TR1, if I'm not mistaken, and will be
part of the next standard. (But I don't follow the library
issues too closely, so I could be wrong here.)
It's possible that the g++ library uses some special g++
extensions here, but somehow I doubt it. And it certainly
can be done in standard C++. (The standard requires it be
done somehow, of course.)
I don't think you really understood what I meant.
The standard says that when one of the two iterator template
functions is instantiated, if the instantiation type is an
integral type (and thus, not an iterator), the instantiation
should behave as if the corresponding non-template function
taking a size_t as its first argument was called. That means
that every implementation must somehow make it work.

At the time this clause was adopted, it was done so because it
was clear to the authors that it could be done in standard C++,
without any compiler tricks.
template<>
struct IntegerDiscriminator< int >
{
typdef True Type ;
}
Doesn't that only take care of the case where the parameter is
of type 'int'? What about unsigned int, long, unsigned long,
short, unsigned short, char, signed char, unsigned char, long
long, unsigned long long...
Obviously. I thought I'd put a ... after it, to indicate that
you needed to extend it.

But you only have to do it once. You can use
IntegerDiscriminator for all such functions.
Or something like that. For all the details, see the
Vandevoorde and Josuttis.
I didn't understand that.
If you're doing anything but the simplest templates, you should
definitely read " C++ Templates - The Complete Guide", by David
Vandevoorde and Nicolai Josuttis (ISBN 0-201-73484-2). It is
the reference for everything concerning templates, and it has
a complete chapter concerning just such problems. It's one of
those musts, without which your technical library isn't
complete.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 21 '08 #7
Could perhaps <tr1/type_traitsbe used for this purpose?
Jul 21 '08 #8
Juha Nieminen wrote:
Could perhaps <tr1/type_traitsbe used for this purpose?
Yes.
Best

Kai-Uwe Bux
Jul 21 '08 #9
Kai-Uwe Bux wrote:
Juha Nieminen wrote:
> Could perhaps <tr1/type_traitsbe used for this purpose?

Yes.
This seems to work. Does anyone have any objections (besides it
using tr1)?

//---------------------------------------------------------------------------
#include <iostream>
#include <list>
#include <tr1/type_traits>

template<typename T>
class MyClass
{
template<typename ValueType>
void dispatchAssign(ValueType amount, ValueType value,
std::tr1::true_type)
{
std::cout << amount << " " << value << std::endl;
}

template<typename InputIterator>
void dispatchAssign(InputIterator first, InputIterator last,
std::tr1::false_type)
{
while(first != last)
{
std::cout << *first << std::endl;
++first;
}
}

public:
void assign(size_t amount, const T& value)
{
dispatchAssign(amount, value, std::tr1::true_type());
}

template<typename InputIterator>
void assign(InputIterator first, InputIterator last)
{
dispatchAssign(first, last,
std::tr1::is_integral<InputIterator>());
}
};

int main()
{
std::list<intl1, l2;
l1.assign(10, 5);
l2.assign(l1.begin(), l1.end());

MyClass<intmc;
mc.assign(l1.begin(), l1.end());
mc.assign(10, 5);
}
//---------------------------------------------------------------------------
Jul 21 '08 #10
On Jul 21, 4:23 pm, Juha Nieminen <nos...@thanks.invalidwrote:
Could perhaps <tr1/type_traitsbe used for this purpose?
Certainly. I'm not sure, but I think the code in the g++
library is inspired from this (or from an earlier Boost
version). If it isn't simply a copy with the names changed.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 22 '08 #11
On 21 jul, 17:17, Juha Nieminen <nos...@thanks.invalidwrote:
Kai-Uwe Bux wrote:
Juha Nieminen wrote:
Could perhaps <tr1/type_traitsbe used for this purpose?
Yes.

This seems to work. Does anyone have any objections (besides it
using tr1)?
Yes. It won't work with types that are not convertable from int.
Try it with this class:
class Bar {};
ostream& operator<<(ostream& os, const Bar&){ os << "Bar";}
>
//---------------------------------------------------------------------------
#include <iostream>
#include <list>
#include <tr1/type_traits>

template<typename T>
class MyClass
{
template<typename ValueType>
void dispatchAssign(ValueType amount, ValueType value,
std::tr1::true_type)
For it to work with other types than integers, this should be
void dispatchAssign(size_t amount, const T& value,
std::tr1::true_type)

<snip>

Bart v Ingen Schenau
Jul 23 '08 #12

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

3 posts views Thread by Alexander Stippler | last post: by
3 posts views Thread by Old Wolf | last post: by
1 post views Thread by Marcin Kaliciñski | last post: by
19 posts views Thread by John | last post: by
2 posts views Thread by ma740988 | last post: by
2 posts views Thread by Ole Nielsby | last post: by
6 posts views Thread by gexarchakos | last post: by
reply views Thread by suresh191 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.