473,386 Members | 1,715 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

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 1388
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 thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: Alexander Stippler | last post by:
Hi, I have to design some two dimensional iterators and I'm not quite sure about the design. I'd like to have the iterators mostly STL-like. The STL does not contain two dimensional iterators, I...
4
by: Leon | last post by:
Hi all. I'm a bit confused about the use of STL iterators and pointers, and I was wondering if maybe you could give me some pointers ;) Is it possible to convert between iterators and pointers...
3
by: Old Wolf | last post by:
Hi all. G++ fails to compile the following: #include <string> int main() { std::string foo("abc=123"); std::string::const_iterator delimiter = std::find(foo.begin(), foo.end(), '=');
1
by: Marcin Kaliciñski | last post by:
template<class RanAccIt> void some_algorithm(RanAccIt begin, RanAccIt end) { // this algorithm involves calling std::lexicographical_compare // on range [begin, end), and on reverse of this range...
19
by: John | last post by:
In STL's map implementation, the distance between two iterators it1, it2 takes O(K) to compute where K is the actual distance between the two iterators. In theory, a red black tree can do this...
2
by: ma740988 | last post by:
typedef std::vector < std::complex < double > > complex_vec_type; // option1 int main() { complex_vec_type cc ( 24000 ); complex_vec_type dd ( &cc, &cc ); } versus
10
by: Pierre Thibault | last post by:
Hello! I am currently trying to port a C++ code to python, and I think I am stuck because of the very different behavior of STL iterators vs python iterators. What I need to do is a simple...
2
by: Ole Nielsby | last post by:
Porting a project from VC8 to GCC, I stumbled on this: I have certain templates that use STL collections with a value type that is a pointer to a template parameter type. When I declare...
6
by: gexarchakos | last post by:
Hi there, Please give me at least a hint... I have a problem implementing a function object with parameters two iterators. That is: A class 'node' produces messages using a routing policy....
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.