The following prints "void", which seems to be what the standard intends.
I expected to see something like "int".
// Deleted headers.
using namespace std;
int main() {
typedef vector<int> IntVec;
typedef back_insert_iterator<IntVec> IntVecBackInserter;
IntVec v;
IntVecBackInserter iter = back_inserter(v);
cout << typeid(iterator_traits<IntVecBackInserter>::value_type).name()
<< endl;
} // main
terry
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/21/2006 8:51:14 PM |
|
Terry G ha scritto:
> The following prints "void", which seems to be what the standard intends.
> I expected to see something like "int".
>
> // Deleted headers.
> using namespace std;
> int main() {
> typedef vector<int> IntVec;
> typedef back_insert_iterator<IntVec> IntVecBackInserter;
> IntVec v;
> IntVecBackInserter iter = back_inserter(v);
> cout << typeid(iterator_traits<IntVecBackInserter>::value_type).name()
> << endl;
> } // main
>
Because back_inserter_iterator<> is an output iterator but not an input
iterator. value_type, which is defined as the type able to hold the
value of the expression *it, makes sense only for input iterators.
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
11/21/2006 10:17:16 PM
|
|
> Because back_inserter_iterator<> is an output iterator but not an input
> iterator. value_type, which is defined as the type able to hold the
> value of the expression *it, makes sense only for input iterators.
What if I want to know what type *it will return?
terry
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/22/2006 3:09:44 AM
|
|
In article <ek092f$mp0$1@news.netins.net>, Terry G
<tjgolubi@netins.net> wrote:
> > Because back_inserter_iterator<> is an output iterator but not an input
> > iterator. value_type, which is defined as the type able to hold the
> > value of the expression *it, makes sense only for input iterators.
>
> What if I want to know what type *it will return?
>
> terry
back_inserter_Iterator does have a nested typedef container_type which
is the type of the container the that is used with the push_back
operation. This effectively gives the value_type of what was pushed
container_type::value_type. Is that what you want, given only a
back_inserter_iterator<C> where C is not stated in your code??
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Carl
|
11/22/2006 11:09:50 AM
|
|
Terry G ha scritto:
>> Because back_inserter_iterator<> is an output iterator but not an input
>> iterator. value_type, which is defined as the type able to hold the
>> value of the expression *it, makes sense only for input iterators.
>
> What if I want to know what type *it will return?
>
The requirement of an output iterator says that the expression "*it = v"
is well-defined and does what it's supposed to do, but it's not said
what the expression "*it" is required to be and even if it can be used
in any way different from "*it = v". Therefore, defining value_type to
be void is a big warning sign that you shouldn't rely on the actual
type. It's like this for your own good. Why do you want to know?
Ganesh
PS: once we have decltype in the language, we could write decltype(*it),
but the question remains: what for?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
11/22/2006 11:40:12 AM
|
|
"Terry G" <tjgolubi@netins.net> wrote in message
news:ek092f$mp0$1@news.netins.net...
>> Because back_inserter_iterator<> is an output iterator but not an input
>> iterator. value_type, which is defined as the type able to hold the
>> value of the expression *it, makes sense only for input iterators.
>
> What if I want to know what type *it will return?
You can only assign to *it and increment it with ++it or it++.
P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
P
|
11/22/2006 11:42:27 AM
|
|
Terry G wrote:
>> Because back_inserter_iterator<> is an output iterator but not an input
>> iterator. value_type, which is defined as the type able to hold the
>> value of the expression *it, makes sense only for input iterators.
>
> What if I want to know what type *it will return?
Who says that *it returns anything useful? (Hint, for output iterators
like back_insert_iterator or insert_iterator, *it is not required to
return anything that can be used in any way other than assigning to it)
--
Clark S. Cox III
clarkcox3@gmail.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Clark
|
11/22/2006 3:32:56 PM
|
|
Ganesh wrote:
> The requirement of an output iterator says that the expression "*it = v"
> is well-defined and does what it's supposed to do, but it's not said
> what the expression "*it" is required to be and even if it can be used
> in any way different from "*it = v". Therefore, defining value_type to
> be void is a big warning sign that you shouldn't rely on the actual
> type. It's like this for your own good. Why do you want to know?
> PS: once we have decltype in the language, we could write decltype(*it),
> but the question remains: what for?
What for? Uh.. well I'm probably trying to do a "bad thing", but...
Say I have a template function
template <class OutIter>
OutIter CopyOut(OutIter iter) {
// What type does user want? I.e. what is OutIter::value_type?
// Some appropriate logic to output internal state to users container.
} // CopyOut
The idea is that I have some internal information that I want to give to my
user.
Previously, I just returned a vector<MyType>, forcing a container, a copy,
and a type.
I wanted to be more flexible, allowing the user to specify any convertible
type, avoid an unnecessary copy,
and let her pick whatever container.
terry
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/22/2006 9:37:27 PM
|
|
Terry G ha scritto:
> Ganesh wrote:
>
> Say I have a template function
>
> template <class OutIter>
> OutIter CopyOut(OutIter iter) {
> // What type does user want? I.e. what is OutIter::value_type?
> // Some appropriate logic to output internal state to users container.
> } // CopyOut
Fact is that OutIter::value_type may not (and usually does not) convey
any useful information. Even decltype(*iter) is not going to provide
useful information for "pure" output iterators, such as
std::back_inserter_iterator. If fact, if you look at the definition of
std::back_inserter_iterator, the type of *iter is...
std::back_inserter_iterator itself! For another kind of output iterator
it might have been any proxy class.
> The idea is that I have some internal information that I want to give to my
> user.
Which kind of information are you thinking about? Could you be more precise?
> Previously, I just returned a vector<MyType>, forcing a container, a copy,
> and a type.
Previously? I'm sorry but I can't follow you there. You did not post any
"previous" code to refer to. How could you expect people to understand?
> I wanted to be more flexible, allowing the user to specify any convertible
> type, avoid an unnecessary copy,
> and let her pick whatever container.
Please provide us a *working* code snippet of what you have but you
don't like and some (obviously not working, but hopefully clarifying)
code of what you would like to have. Otherwise we (both you and I) are
just losing our time.
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
11/22/2006 11:42:10 PM
|
|
In article <4qV8h.51940$uv5.351593@twister1.libero.it>,
Alberto Ganesh Barbati <AlbertoBarbati@libero.it> wrote:
> Terry G ha scritto:
> >> Because back_inserter_iterator<> is an output iterator but not an input
> >> iterator. value_type, which is defined as the type able to hold the
> >> value of the expression *it, makes sense only for input iterators.
> >
> > What if I want to know what type *it will return?
> >
>
> The requirement of an output iterator says that the expression "*it = v"
> is well-defined and does what it's supposed to do, but it's not said
> what the expression "*it" is required to be and even if it can be used
> in any way different from "*it = v". Therefore, defining value_type to
> be void is a big warning sign that you shouldn't rely on the actual
> type. It's like this for your own good. Why do you want to know?
>
> Ganesh
>
> PS: once we have decltype in the language, we could write decltype(*it),
> but the question remains: what for?
I know why *I* want output iterators to have value_type. Because I want
to write generic code like:
template <class It>
It
format(char val, It first, It last)
{
typedef typename std::iterator_traits<It>::value_type value_type;
if (first == last)
throw format_error();
*first = static_cast<value_type>(val);
return ++first;
}
I.e. I don't want the return type of *it (which should be it::reference
anyway, and it is fine with me if *it returns void). I want a type such
that if I convert my value v to that type, I'm assured of a clean
assignment into the output iterator.
Note that I'm also wanting output iterators to be equality comparable.
In the case of back_insert_iterator, the following definitions would be
fine:
bool operator==(const back_insert_iterator&,
const back_insert_iterator&)
{return false;}
bool operator!=(const back_insert_iterator&,
const back_insert_iterator&)
{return true;}
With such changes I could safely format a char into any "iterator
delimited stream".
template <class Container>
class back_insert_iterator
{
protected:
Container* container;
public:
typedef Container container_type;
typedef output_iterator_tag iterator_category;
typedef typename container_type::value_type value_type;
typedef ptrdiff_t difference_type;
typedef typename container_type::pointer pointer;
typedef back_insert_iterator& reference;
explicit back_insert_iterator(Container& x) : container(&x) {}
back_insert_iterator& operator=(const value_type& value)
{container->push_back(value); return *this;}
back_insert_iterator& operator=(value_type&& value)
{container->push_back(std::move(value)); return *this;}
reference operator*() {return *this;}
reference operator++() {return *this;}
reference operator++(int) {return *this;}
friend bool operator==(const back_insert_iterator&,
const back_insert_iterator&) {return false;}
friend bool operator!=(const back_insert_iterator&,
const back_insert_iterator&) {return true;}
};
-Howard
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Howard
|
11/22/2006 11:56:09 PM
|
|
In article
<howard.hinnant-BBDDCE.12010822112006@syrcnyrdrs-02-ge0.nyroc.rr.com>,
Howard Hinnant <howard.hinnant@gmail.com> wrote:
> In article <4qV8h.51940$uv5.351593@twister1.libero.it>,
> Alberto Ganesh Barbati <AlbertoBarbati@libero.it> wrote:
>
> > Terry G ha scritto:
> > >> Because back_inserter_iterator<> is an output iterator but not an input
> > >> iterator. value_type, which is defined as the type able to hold the
> > >> value of the expression *it, makes sense only for input iterators.
> > >
> > > What if I want to know what type *it will return?
> > >
> >
> > The requirement of an output iterator says that the expression "*it = v"
> > is well-defined and does what it's supposed to do, but it's not said
> > what the expression "*it" is required to be and even if it can be used
> > in any way different from "*it = v". Therefore, defining value_type to
> > be void is a big warning sign that you shouldn't rely on the actual
> > type. It's like this for your own good. Why do you want to know?
> >
> > Ganesh
> >
> > PS: once we have decltype in the language, we could write decltype(*it),
> > but the question remains: what for?
>
> I know why *I* want output iterators to have value_type. Because I want
> to write generic code like:
>
> template <class It>
> It
> format(char val, It first, It last)
> {
> typedef typename std::iterator_traits<It>::value_type value_type;
> if (first == last)
> throw format_error();
> *first = static_cast<value_type>(val);
> return ++first;
> }
>
> I.e. I don't want the return type of *it (which should be it::reference
> anyway, and it is fine with me if *it returns void). I want a type such
> that if I convert my value v to that type, I'm assured of a clean
> assignment into the output iterator.
>
> Note that I'm also wanting output iterators to be equality comparable.
> In the case of back_insert_iterator, the following definitions would be
> fine:
>
> bool operator==(const back_insert_iterator&,
> const back_insert_iterator&)
> {return false;}
>
> bool operator!=(const back_insert_iterator&,
> const back_insert_iterator&)
> {return true;}
>
> With such changes I could safely format a char into any "iterator
> delimited stream".
>
> template <class Container>
> class back_insert_iterator
> {
> protected:
> Container* container;
>
> public:
> typedef Container container_type;
well [N2134=06-0204 ]
24.4.2.1 Class template back_insert_iterator
namespace std {
template <class Container>
class back_insert_iterator :
public iterator<output_iterator_tag,void,void,void,void>
{
protected:
Container* container;
public:
typedef Container container_type;
// ,,,
};
template <class It,class Tag>
struct ValueType
{
typedef typename std::iterator_traits<It>::value_type type;
};
template <class It>
struct ValueType<It,std::input_iterator_tag>
{
typedef typename It::container_type::value_type type;
};
template <class It>
It format(char val,It first,It last)
{
typedef ValueType<It>::type value_type;
// ,,,
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Carl
|
11/23/2006 3:02:26 AM
|
|
Ganesh wrote:
>> Previously, I just returned a vector<MyType>, forcing a container, a
>> copy,
>> and a type.
>
> Previously? I'm sorry but I can't follow you there. You did not post any
> "previous" code to refer to. How could you expect people to understand?
>
>> I wanted to be more flexible, allowing the user to specify any
>> convertible
>> type, avoid an unnecessary copy,
>> and let her pick whatever container.
>
> Please provide us a *working* code snippet of what you have but you
> don't like and some (obviously not working, but hopefully clarifying)
> code of what you would like to have. Otherwise we (both you and I) are
> just losing our time.
Sorry, I've been guilty of that on several posts recently. I'm in a hurry.
Here's some runnable code that illustrates my point.
It contains my previous code, that used a vector<MyType>, where MyType is
uint8_t.
I wanted to allow the user to use the container of her choice and a
reasonable type too.
Thats the "current" version.
Then, I show the code that I wish would work, but it doesn't because
value_type for back_inserter is void.
Thanks for your help.
terry
======================================
#undef KLUDGE // Define this to make this program compile.
#include <vector>
#include <list>
#include <cstddef>
#include <iterator>
#include <algorithm>
#include <iostream>
using namespace std;
typedef unsigned char uint8_t;
typedef vector<uint8_t> MyType;
typedef vector<unsigned> UserType;
// Here's what I had before.
vector<MyType> CopyOut(const uint8_t* src, size_t size,
unsigned count)
{
vector<MyType> rval;
rval.reserve(count);
// Align values on 32-bit boundaries.
size_t stride = 4*((size+3)/4);
while (count--) {
rval.push_back(MyType(src, src+size));
src += stride;
}
return rval;
} // CopyOut
// Here's what I have now.
template <class T, class OutIter>
OutIter CopyOut(OutIter dst, const uint8_t* src, size_t size,
unsigned count)
{
// Align values on 32-bit boundaries.
size_t stride = 4*((size+3)/4);
while (count--) {
*dst++ = T(src, src+size);
src += stride;
}
return dst;
} // CopyOut
// Here's what I wish would work. Doesn't need T parameter.
template <class OutIter>
OutIter CopyOutWish(OutIter dst, const uint8_t* src, size_t size,
unsigned count)
{
#ifndef KLUDGE
// Oops! iterator_traits<OutIter>::value_type is 'void'.
typedef typename iterator_traits<OutIter>::value_type DstType;
#else
typedef UserType DstType;
#endif
// Align values on 32-bit boundaries.
size_t stride = 4*((size+3)/4);
while (count--) {
*dst++ = DstType(src, src+size);
src += stride;
}
return dst;
} // CopyOutWish
struct PseudoMemory {
uint8_t data[64];
PseudoMemory() {
for (int i=0; i!=sizeof(data); ++i)
data[i] = uint8_t(i);
} // ctor
}; // PseudoMemory;
PseudoMemory Memory;
void PrintMine(const MyType& x) {
copy(x.begin(), x.end(), ostream_iterator<unsigned>(cout, " "));
cout << endl;
} // PrintMine
void PrintUser(const UserType& x) {
copy(x.begin(), x.end(), ostream_iterator<unsigned>(cout, " "));
cout << endl;
} // PrintUser
int main() {
cout << endl << "Demonstrate previous method." << endl;
vector<MyType> myResult = CopyOut(&Memory.data[4], 3, 5);
for_each(myResult.begin(), myResult.end(), PrintMine);
cout << endl << "Demonstrate current method." << endl;
list<UserType> userResult;
(void) CopyOut<UserType>(back_inserter(userResult),
&Memory.data[4], 3, 5);
for_each(userResult.begin(), userResult.end(), PrintUser);
cout << endl << "Demonstrate wishful method." << endl;
userResult.clear();
(void) CopyOutWish(back_inserter(userResult),
&Memory.data[4], 3, 5);
for_each(userResult.begin(), userResult.end(), PrintUser);
return EXIT_SUCCESS;
} // main
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/23/2006 3:02:56 AM
|
|
Howard wrote:
> I know why *I* want output iterators to have value_type. Because I want
> to write generic code like:
>
> template <class It>
> It
> format(char val, It first, It last)
> {
> typedef typename std::iterator_traits<It>::value_type value_type;
> if (first == last)
> throw format_error();
> *first = static_cast<value_type>(val);
> return ++first;
> }
>
> I.e. I don't want the return type of *it (which should be it::reference
> anyway, and it is fine with me if *it returns void). I want a type such
> that if I convert my value v to that type, I'm assured of a clean
> assignment into the output iterator.
>
> Note that I'm also wanting output iterators to be equality comparable.
> In the case of back_insert_iterator, the following definitions would be
> fine: <snipped>
Exactly what I want, but you say it better.
I expected the value_type of the back_insert_iterator to be the value_type
of the associated container.
Actually, this is desirable for almost any output iterator, not just the
back_insert_iterator.
For some output iterators, value_type should be void when nothing else
makes
sense.
This seems intuitive to me.
A question: why do you prefer static_cast<value_type>(val)
instead of using the function-like form: value_type(val)?
Is there any significant difference?
terry
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/23/2006 3:03:56 AM
|
|
On 21 Nov 2006 17:17:16 -0500, Alberto Ganesh Barbati wrote:
>Because back_inserter_iterator<> is an output iterator but not an input
>iterator. value_type, which is defined as the type able to hold the
>value of the expression *it, makes sense only for input iterators.
In effect there was some uncertainty about this, clarified with
library issue 324. The documentation of the (brain-dead)
to_block_range() function of boost::dynamic_bitset<> should still be
fixed in this regard (though that's probably the smallest of
dynamic_bitset's problems).
--
Gennaro Prota
[To mail me, remove any 'u' from the provided address]
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Gennaro
|
11/23/2006 3:08:27 AM
|
|
Terry G ha scritto:
>
> Sorry, I've been guilty of that on several posts recently. I'm in a hurry.
> Here's some runnable code that illustrates my point.
> It contains my previous code, that used a vector<MyType>, where MyType is
> uint8_t.
> I wanted to allow the user to use the container of her choice and a
> reasonable type too.
> Thats the "current" version.
> Then, I show the code that I wish would work, but it doesn't because
> value_type for back_inserter is void.
>
> <code snipped>>
>
Much better! ;-)
Maybe this could be a solution:
template <class T>
struct true_value_type
{
typedef typename std::iterator_traits<T>::value_type type;
};
template <class T>
struct true_value_type<std::back_insert_iterator<T> >
{
typedef typename T::value_type type;
};
then you just use
template <class OutIter>
OutIter CopyOutWish(OutIter dst, const uint8_t* src, size_t size,
unsigned count)
{
typedef typename true_value_type<OutIter>::type DstType;
/* */
}
HTH,
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
11/23/2006 12:04:43 PM
|
|
Terry G ha scritto:
>
> A question: why do you prefer static_cast<value_type>(val)
> instead of using the function-like form: value_type(val)?
> Is there any significant difference?
>
The function-like form with one argument is actually equivalent to the
C-style cast (value_type)(val) so it could apply either a static_cast or
a reinterpret_cast (plus an additional const_cast).
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
11/23/2006 12:07:42 PM
|
|
Ganesh suggests:
> template <class T>
> struct true_value_type
> {
> typedef typename std::iterator_traits<T>::value_type type;
> };
>
> template <class T>
> struct true_value_type<std::back_insert_iterator<T> >
> {
> typedef typename T::value_type type;
> };
>
> then you just use
>
> template <class OutIter>
> OutIter CopyOutWish(OutIter dst, const uint8_t* src, size_t size,
> unsigned count)
> {
> typedef typename true_value_type<OutIter>::type DstType;
> /* */
> }
I like that and will use it. Thanks.
I imagine that this will become more of a problem for more people in the
near future.
More people are caring about such things.
If value_type is inappropriate for this, should another standard iterator
trait be defined for this purpose?
I would have called such a trait "value_type".
Apparently, "value_type" should have been named "pointee_type".
Then, I wouldn't have been confused.
How about "deref_type"?
I'm in a hurry again, but this would be cool..
typedef typeof(*OutIter) OutType;
Something like typeof() would make a whole lot of clever template
programming easier.
Gotta run! I'm late for Thanksgiving dinner.
terry
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/23/2006 6:40:22 PM
|
|
Howard Hinnant <howard.hinnant@gmail.com> writes:
| In article <4qV8h.51940$uv5.351593@twister1.libero.it>,
| Alberto Ganesh Barbati <AlbertoBarbati@libero.it> wrote:
|
| > Terry G ha scritto:
| > >> Because back_inserter_iterator<> is an output iterator but not an
input
| > >> iterator. value_type, which is defined as the type able to hold the
| > >> value of the expression *it, makes sense only for input iterators.
| > >
| > > What if I want to know what type *it will return?
| > >
| >
| > The requirement of an output iterator says that the expression "*it = v"
| > is well-defined and does what it's supposed to do, but it's not said
| > what the expression "*it" is required to be and even if it can be used
| > in any way different from "*it = v". Therefore, defining value_type to
| > be void is a big warning sign that you shouldn't rely on the actual
| > type. It's like this for your own good. Why do you want to know?
| >
| > Ganesh
| >
| > PS: once we have decltype in the language, we could write decltype(*it),
| > but the question remains: what for?
|
| I know why *I* want output iterators to have value_type. Because I want
| to write generic code like:
|
| template <class It>
| It
| format(char val, It first, It last)
| {
| typedef typename std::iterator_traits<It>::value_type value_type;
| if (first == last)
| throw format_error();
| *first = static_cast<value_type>(val);
This is not meant to pick on you. But, I don't want to generic code like
that. And I would not want C++ programmers to get exposed to that kind
of code as the sort of things people write in generic programmng :-)
I would like to write a generic format() that does not require a cast
(whether static or not).
--
Gabriel Dos Reis
gdr@integrable-solutions.net
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Gabriel
|
11/23/2006 11:42:23 PM
|
|
Terry G ha scritto:
> If value_type is inappropriate for this, should another standard iterator
> trait be defined for this purpose?
> I would have called such a trait "value_type".
> Apparently, "value_type" should have been named "pointee_type".
> Then, I wouldn't have been confused.
> How about "deref_type"?
In the standard library, "value_type" is a type that can hold the result
of *it. It sounds good to me and it's consistent with the value_type
typedef of containers (in the sense that the value_type of a container
and of its iterators is the same except possibly for const qualifiers).
I find it good that value_type is void for "pure" output iterators
because the expression "*it" is not intended to have a "storable" value.
In standardese: "The only valid use of an operator* is on the left side
of the assignment statement."
So I find value_type to be properly defined, useful and correctly named.
About "your" trait, notice that there may be output iterators for which
there is no unique way to define it. The standard requires that "*it =
o" is defined for some "o", but does *not* require that there must be
exactly *one type* of "o" that can be used in such expression.
For example it is quite easy to write one ostream_iterator-like class
that is able to write onto a stream any type for which operator<< is
properly overloaded.
So in order to define your trait you have to restrict to a subset of
output iterators and this automatically makes the trait less useful. How
to call it? The first two names that come to my mind are source_type and
source_value_type.
> I'm in a hurry again, but this would be cool..
>
> typedef typeof(*OutIter) OutType;
>
> Something like typeof() would make a whole lot of clever template
> programming easier.
They're working on it and it seems there's good chance that it will be
included in the next revision of C++. The name is probably going to
declspec, not typeof. Notice that in the case of "pure" output iterators
declspec(*it) is still useless, because it's the type on right hand side
of the "=" that you are interested into, but *it lies on the left side
and is probably going to be some uninteresting proxy object.
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
11/24/2006 1:04:50 AM
|
|
> Terry G ha scritto:
>>
>> A question: why do you prefer static_cast<value_type>(val)
>> instead of using the function-like form: value_type(val)?
>> Is there any significant difference?
>>
> Ganesh replied
> The function-like form with one argument is actually equivalent to the
> C-style cast (value_type)(val) so it could apply either a static_cast or
> a reinterpret_cast (plus an additional const_cast).
Terry had a spaz!
What!? I thought the function-style cast was equivalent to static_cast.
I read the C++ spec and its not yet clear to me.
It seems that for built-in types, type(x) is equivalent to (type) x.
That's
scary.
But what about for non-built-in types. Is type(x) equivalent to
static_cast<type>(x)?
Should I stop using function-style casts altogether?
terry
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/24/2006 4:00:15 AM
|
|
> This is not meant to pick on you. But, I don't want to generic code like
> that. And I would not want C++ programmers to get exposed to that kind
> of code as the sort of things people write in generic programmng :-)
> I would like to write a generic format() that does not require a cast
> (whether static or not).
Someone has to convert bits to higher level constructs.
Generic programming is valuable for that.
Casts are unavoidable for this, no?
terry
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/24/2006 4:01:15 AM
|
|
"Terry G" <tjgolubi@netins.net> writes:
| > This is not meant to pick on you. But, I don't want to generic code
like
| > that. And I would not want C++ programmers to get exposed to that kind
| > of code as the sort of things people write in generic programmng :-)
| > I would like to write a generic format() that does not require a cast
| > (whether static or not).
|
| Someone has to convert bits to higher level constructs.
| Generic programming is valuable for that.
So is conventional procedural programming :-)
| Casts are unavoidable for this, no?
The code shown used a static_cast, not a reinterpret_cast -- so in
fact, it was restricting the space of allowed casts; it isn't "generic"
if I may say. The question therefore is why the static_cast is better
than simple
*it = val;
--
Gabriel Dos Reis
gdr@integrable-solutions.net
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Gabriel
|
11/24/2006 9:14:21 AM
|
|
Terry G wrote:
>> Terry G ha scritto:
>>> A question: why do you prefer static_cast<value_type>(val)
>>> instead of using the function-like form: value_type(val)?
>>> Is there any significant difference?
>>>
>> Ganesh replied
>> The function-like form with one argument is actually equivalent to the
>> C-style cast (value_type)(val) so it could apply either a static_cast or
>> a reinterpret_cast (plus an additional const_cast).
>
> Terry had a spaz!
>
> What!? I thought the function-style cast was equivalent to static_cast.
> I read the C++ spec and its not yet clear to me.
> It seems that for built-in types, type(x) is equivalent to (type) x.
> That's
> scary.
> But what about for non-built-in types. Is type(x) equivalent to
> static_cast<type>(x)?
> Should I stop using function-style casts altogether?
"type(x)" casts are always equivalent to "(type)x" casts.
>From "5.2.3 Explicit type conversion (functional notation)":
A simple-type-specifier (7.1.5) followed by a parenthesized
expression-list constructs a value of the specified type given
the expression list. If the expression list is a single expression,
the type conversion expression is equivalent (in definedness, and
if defined in meaning) to the corresponding cast expression (5.4).
"5.4 Explicit type conversion (cast notation)" describes the "(type)x"
style casts.
--
Clark S. Cox III
clarkcox3@gmail.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Clark
|
11/24/2006 9:14:43 AM
|
|
Terry G wrote:
> > Terry G ha scritto:
> >> A question: why do you prefer static_cast<value_type>(val)
> >> instead of using the function-like form: value_type(val)?
> >> Is there any significant difference?
> > Ganesh replied
> > The function-like form with one argument is actually equivalent to the
> > C-style cast (value_type)(val) so it could apply either a static_cast or
> > a reinterpret_cast (plus an additional const_cast).
> Terry had a spaz!
> What!? I thought the function-style cast was equivalent to static_cast.
> I read the C++ spec and its not yet clear to me.
�5.2.3/2 (Explicit type conversion (functional notation): "If
the expression list is a single expression, the type conversion
expression is equivalent (in de- finedness, and if defined in
meaning) to the corresponding cast expression (5.4)." And in
�5.4/5:
The conversions performed by
-- a const_cast (5.2.11),
-- a static_cast (5.2.9),
-- a static_cast followed by a const_cast,
-- a reinterpret_cast (5.2.10), or
-- a reinterpret_cast followed by a const_cast,
can be performed using the cast notation of explicit type
conversion.
[...]
If a conversion can be interpreted in more than one of the
ways listed above, the interpretation that appears first in
the list is used[...]
> It seems that for built-in types, type(x) is equivalent to
> (type) x. That's scary.
> But what about for non-built-in types. Is type(x) equivalent to
> static_cast<type>(x)?
The differences always involve references and/or pointers, I
think---all reinterpret_cast have either a reference or a
pointer as either the target type or the source type. But when
casting between pointer types or reference types, you have to be
exceedingly careful:
struct Base1 {} ;
struct Base2 {} ;
struct Derived : Base1, Base2 {} ;
typedef Base1* B1Ptr ;
typedef Base2* B2Ptr ;
typedef Derived* DPtr ;
B1Ptr pB1 ;
B2Ptr pB2 ;
DPtr pD ;
pB1 = B1Ptr( pD ) ; // static_cast
pB2 = B2Ptr( pD ) ; // static_cast
pD = DPtr( pB1 ) ; // static_cast
pD = DPtr( pB2 ) ; // static_cast
pB1 = B1Ptr( pB2 ) ; // reinterpret_cast
pB2 = B2Ptr( pB1 ) ; // reinterpret_cast
> Should I stop using function-style casts altogether?
It's a style question. I (and I suspect most people) use them
rather freely when converting to a class type; the syntax is
parallel to that used when constructing with more than one
argument, or with no arguments. I tend to use C style casts
when converting between arithmetic types, or when converting a
class (which has a user defined conversion operator) to an
arithmetic type, but I'm not necessarily systematic about it;
I'll sometimes use a static_cast as well. I will never use
anything but static_cast (or reinterpret_cast, if that's what I
want) when pointers or references are involved on either side
of the cast. (And I don't typedef pointers or references, so
that it is always clear when this is the case.)
--
James Kanze (GABI Software) email:james.kanze@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
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
James
|
11/24/2006 9:24:40 AM
|
|
Terry G ha scritto:
>> Terry G ha scritto:
>>> A question: why do you prefer static_cast<value_type>(val)
>>> instead of using the function-like form: value_type(val)?
>>> Is there any significant difference?
>>>
>> Ganesh replied
>> The function-like form with one argument is actually equivalent to the
>> C-style cast (value_type)(val) so it could apply either a static_cast or
>> a reinterpret_cast (plus an additional const_cast).
>
> Terry had a spaz!
I had it too, when they told me the first time.
> What!? I thought the function-style cast was equivalent to static_cast.
> I read the C++ spec and its not yet clear to me.
> It seems that for built-in types, type(x) is equivalent to (type) x.
> That's scary.
> But what about for non-built-in types. Is type(x) equivalent to
> static_cast<type>(x)?
5.2.3/1 does not mention built-in types, so it applies to all types:
"If the expression list is a single expression, the type conversion
expression is equivalent (in definedness, and if defined in meaning) to
the corresponding cast expression (5.4)."
However, reinterpret_cast can only perform the conversions listed in
5.2.10, in particular it can't convert anything to a user defined type T
unless T is a reference type. Notice that the only way to write a
conversion to a reference type using function-style syntax is by using a
typedef and that rarely happens in non-generic code.
> Should I stop using function-style casts altogether?
If the target type is a non-pointer, non-reference UDT or a floating
point type, then it's safe to use, because neither reinterpret_cast nor
const_cast will apply in those cases. Ditto for integral types unless
the source type is a pointer type. If the target type is either a
pointer type (any kind, including pointers to members) or a reference
type or if there's any chance that it might be so (for example in
generic code) then I would avoid the function-style in favor of an
explicit use of either static_cast or reinterpret_cast.
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
11/24/2006 10:44:45 AM
|
|
Gabriel Dos Reis wrote:
[...]
> The code shown used a static_cast, not a reinterpret_cast -- so in
> fact, it was restricting the space of allowed casts; it isn't "generic"
> if I may say. The question therefore is why the static_cast is better
> than simple
>
> *it = val;
Possibly because it ensures that no warnings are emitted for the cases
where the "castless" statement is legal.
Cheers,
Nicola Musatti
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Nicola
|
11/24/2006 10:28:44 PM
|
|
Gabriel Dos Reis wrote:
> The code shown used a static_cast, not a reinterpret_cast -- so in
> fact, it was restricting the space of allowed casts; it isn't "generic"
> if I may say. The question therefore is why the static_cast is better
> than simple
>
> *it = val;
Ok, in my example (sorry I don't know how to link to another message),
I needed something like:
const uint8_t* start;
std::size_t len;
*it = T(start, start+len);
but I wanted the generic program to infer T from the iterator.
Here's the actual code from the library I'm creating.
This is a low-level routine, used to copy an array of fixed-size data
from a
message received over a wire.
Each data element is aligned on a 32-bit boundary (an external requirement).
Should I be doing something else, i.e. is my design fundamentally stupid?
In the past, I just provided a void* interface and everyone was happy.
Then I provided a vector<vector<uint8_t>> return value, which would copy
the
data (twice?).
Now, I'm trying to do better (more generic, more efficient), but I have the
feeling I'm off track here.
What should the interface be for this sort of low-level task, i.e. copying
bytes out of a received message?
terry
template <class T, class OutIter>
static OutIter CopyOut(OutIter dst, const uint8_t* src, std::size_t size,
unsigned count)
{
if (count == 0)
return dst;
*dst = T(src, src+size);
if (--count) {
std::size_t stride = 4*((size+3)/4);
do {
src += stride;
*++dst = T(src, src+size);
} while (--count);
}
return ++dst;
} // CopyOut
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Terry
|
11/25/2006 6:12:06 AM
|
|
"Nicola Musatti" <nicola.musatti@gmail.com> writes:
| Gabriel Dos Reis wrote:
| [...]
| > The code shown used a static_cast, not a reinterpret_cast -- so in
| > fact, it was restricting the space of allowed casts; it isn't "generic"
| > if I may say. The question therefore is why the static_cast is better
| > than simple
| >
| > *it = val;
|
| Possibly because it ensures that no warnings are emitted for the cases
| where the "castless" statement is legal.
Such as?
--
Gabriel Dos Reis
gdr@integrable-solutions.net
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Gabriel
|
11/25/2006 11:18:57 AM
|
|
Nicola Musatti ha scritto:
> Gabriel Dos Reis wrote:
> [...]
>> The code shown used a static_cast, not a reinterpret_cast -- so in
>> fact, it was restricting the space of allowed casts; it isn't "generic"
>> if I may say. The question therefore is why the static_cast is better
>> than simple
>>
>> *it = val;
>
> Possibly because it ensures that no warnings are emitted for the cases
> where the "castless" statement is legal.
Which can be either good or bad. As the code is generic, you don't know
a priori whether the caller is aware of the conversion and thus can
safely ignore the warning or is actually making a mistake that would be
exposed by it.
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
11/25/2006 11:23:26 AM
|
|
|
28 Replies
323 Views
(page loaded in 0.206 seconds)
|