Prev: The D Programming Language
Next: CRTP question
From: Carl Barron on 22 Nov 2006 22:02 In article <howard.hinnant-BBDDCE.12010822112006(a)syrcnyrdrs-02-ge0.nyroc.rr.com>, Howard Hinnant <howard.hinnant(a)gmail.com> wrote: > In article <4qV8h.51940$uv5.351593(a)twister1.libero.it>, > Alberto Ganesh Barbati <AlbertoBarbati(a)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! ]
From: Terry G on 22 Nov 2006 22:03 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! ]
From: Gennaro Prota on 22 Nov 2006 22:08 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! ]
From: Terry G on 22 Nov 2006 22:02 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! ]
From: Alberto Ganesh Barbati on 23 Nov 2006 07:07
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! ] |