Bring Back the Obvious Definition of count()

Similar documents
Concepts for the C++0x Standard Library: Iterators

Iterator Concepts for the C++0x Standard Library

Concepts for the C++0x Standard Library: Iterators (Revision 2)

The following is an excerpt from Scott Meyers new book, Effective C++, Third Edition: 55 Specific Ways to Improve Your Programs and Designs.

Object-Oriented Programming for Scientific Computing

Iterators and Allocators

Object-Oriented Programming for Scientific Computing

Lecture 21 Standard Template Library. A simple, but very limited, view of STL is the generality that using template functions provides.

More STL algorithms (revision 2)

More STL algorithms. Design Decisions. Doc No: N2569= Reply to: Matt Austern Date:

New Iterator Concepts

Working Draft, Technical Specification for C++ Extensions for Parallelism

CS11 Advanced C++ Spring 2018 Lecture 2

A. Koenig, "Templates and Generic Algorithms," Journal of Object-Oriented Programming, June 1994, pp

Doc. No.: WG21/N0937=X3J16/ Date: 28 May 1996 Project: C++ Standard Library Reply to: Nathan Myers

Proposed resolution for US104: Allocator-aware regular expressions (rev 3)

SFU CMPT Topic: Function Objects

Proposed resolution for US104: Allocator-aware regular expressions (rev 3)

use static size for this buffer

Proposed resolution for US104: Allocator-aware regular expressions (rev 4)

This chapter serves mainly to gather and organize information about iterators. Some new concepts are also introduced for completeness.

Iterator Facade. Table of Contents. Overview. Usage. Iterator Core Access operator[] operator-> Reference

Item 4: Extensible Templates: Via Inheritance or Traits?

QUIZ. What is wrong with this code that uses default arguments?

Concepts for the C++0x Standard Library: Introduction

CS11 Advanced C++ Fall Lecture 1

A Parallel Algorithms Library N3724

Evaluation Issues in Generic Programming with Inheritance and Templates in C++

Today. andyoucanalsoconsultchapters6amd7inthetextbook. cis15-fall2007-parsons-lectvii.1 2

C The new standard

Contract Programming For C++0x

Explicit Conversion Operator Draft Working Paper

Instantiation of Template class

G52CPP C++ Programming Lecture 20

Let return Be Direct and explicit

Interview Questions of C++

Fundamentals of Type-Dependent Code Reuse in C++ Mark Isaacson

Apply the following edits to N4741, the working draft of the Standard. The feature test macros cpp_lib_latch and cpp_lib_barrier should be created.

A Standard flat_map. 1 Revisions. 2 Introduction. 3 Motivation and Scope. 1.1 Changes from R Changes from R0

Definitions of scalar type and fundamental type

C++ (7. Vorlesung) Exercise Discussion, Advanced Templates

by Pearson Education, Inc. All Rights Reserved. 2

Concepts Design choices for template argument checking

Document Number: P0429R4 Date: Reply to: 0.1 Revisions... 1

Topics. bool and string types input/output library functions comments memory allocation templates classes

STL in Action: Helper Algorithms

Writing Generic Functions. Lecture 20 Hartmut Kaiser hkaiser/fall_2013/csc1254.html

An Incomplete Language Feature

Fast Introduction to Object Oriented Programming and C++

The Ada Standard Generic Library (SGL)

A RISC class (extended abstract)

Document No.: J16/ WG21/N1098 Date: 17 July Programming Language C++

C++ Programming Lecture 6 Software Engineering Group

Standard Library Reference

Generic Programming in C++: A modest example

C++ Modern and Lucid C++ for Professional Programmers

AN OVERVIEW OF C++ 1

Outline. Function calls and results Returning objects by value. return value optimization (RVO) Call by reference or by value?

How the Adapters and Binders Work

The C Programming Language

Aggregation headers. Introduction

memory_resource_ptr: A Limited Smart Pointer for memory_resource Correctness

Layout-compatibility and Pointer-interconvertibility Traits

Subscripts and sizes should be signed

Programming with Haiku

Short Notes of CS201

MODULE 35 --THE STL-- ALGORITHM PART III

1. Describe History of C++? 2. What is Dev. C++? 3. Why Use Dev. C++ instead of C++ DOS IDE?

Motivation was to facilitate development of systems software, especially OS development.

CS201 - Introduction to Programming Glossary By

The Cut and Thrust of CUDA

Type Inference auto for Note: Note:

Overload Resolution. Ansel Sermersheim & Barbara Geller Amsterdam C++ Group March 2019

Concepts Lite. Constraining Template Arguments with Predicates. Andrew Su9on, Bjarne Stroustrup, Gabriel Dos Reis Texas A&M University

A Proposal for the World s Dumbest Smart Pointer, v2

STUDY NOTES UNIT 1 - INTRODUCTION TO OBJECT ORIENTED PROGRAMMING

I m sure you have been annoyed at least once by having to type out types like this:

STL Exception Handling Contract

CS11 Introduction to C++ Fall Lecture 1

CS3157: Advanced Programming. Outline

COEN244: Class & function templates

III. Classes (Chap. 3)

CS93SI Handout 04 Spring 2006 Apr Review Answers

Direction for C++0x. Bjarne Stroustrup. Texas A&M University (and AT&T Research)

Practice question Answers

Introduction to C++ Introduction. Structure of a C++ Program. Structure of a C++ Program. C++ widely-used general-purpose programming language

Outline. 1 About the course

CS

2 is type double 3 auto i = 1 + 2; // evaluates to an 4 integer, so it is int. 1 auto d = 5.0; // 5.0 is a double literal, so it

C++ Programming Lecture 7 Software Engineering Group

Lab 6. Out: Wednesday 9 March 2005

Concepts for C++0x. 1 Overview 2. 2 Review of generic programming and its terminology 3

Introduction to C++ with content from

Figure 1: Overview of Introspection 2

A brief introduction to C++

CS11 Advanced C++ Fall Lecture 3

Proposal to Acknowledge that Garbage Collection for C++ is Possible X3J16/ WG21/N0932. Bjarne Stroustrup. AT&T Research.

Template Issue Resolutions from the Stockholm Meeting

CS11 Intro C++ Spring 2018 Lecture 1

P0591r4 Utility functions to implement uses-allocator construction

Transcription:

Bring Back the Obvious Definition of count() Bjarne Stroustrup AT&T Bell Laboratories Murray Hill, New Jersey 07974 Alex Stepanov, Matt Austern Silicon Graphics Inc. ABSTRACT #X3J16/96-0029 WG21/N0847 The current definition of the count() algorithm is anomalous in the library, and unnecessary hard to teach and use. I propose to replace it with a version based on the original STL definition of count(). The solution to the count() problem introduces the notion of iterator_traits which allows a significant simplification of the internals of STL. 1 The Problem The count() algorithm is arguably the easiest algorithm to introduce. Even for_each() is more complicated because you have to explain how the function is called for an element. Relying on memory and pure logic, I wrote: void f(list<int>& lc, char cs[], int sz) int i1 = count(lc.begin(),lc.end(),7); int i2 = count(cs,cs+sz, z ); Like me, experts will recognize the mistake from when they did it last time. Please realize, though, that this did work in the original STL. We now have to write: void f(list<int>& lc, char cs[], int sz) int i1 = 0; // do remember to initialize, or else! int i2 = 0; count(lc.begin(),lc.end(),7,i1); count(cs,cs+sz, z,i2); Yuck! This eliminates count() as a candidate for introducing algorithms to novices. The calling interface for count() is simply different (and inferior) from all other standard algorithms. Unless we do something about it, we will have to explain this special case forever and apologize for it each time. The problem is that count() cannot deduce its return type from its iterator type. The reason it cannot do that is that the iterator can be either a class object or a pointer. At the time we generalized the STL to use allocators, this problem was insurmountable. What I propose is to restore the original intuitive interface to count(). 2 A Solution Here is a minimal solution based on partial specialization:

- 2 - template <class Iterator> struct itrait typedef Iterator::distance_type dist; template <class T> struct itrait<t*> typedef ptrdiff_t dist; template <class In, class T> itrait<in>::dist count(in first, In last, T x) itrait<in>::dist n = 0; while (first!= last) if (*first++ == x) ++n; return n; Given this definition of count(), my example works as originally written (or it would have, had my compiler supported partial specialization). This definition brings count() into line with every other standard algorithm. 3 Alternatives I see three alternatives: [1] Do nothing, and leave count() an awkward special case. [2] Add the outlined version of count() to the library, leaving the current version for compatibility. [3] Replace the current count() with the version outlined here. My preference is [3]. The pure addition, [2], is ugly, but manageable; the only purpose for keeping the old version is to keep old code working and old teaching material valid. Because the old versions have 4 arguments and the new 3, there will be no overloading problems. Status quo, [1], has no defense except that it is status quo. In case of [1], I and probably others will recommend use of the version of count() defined above, but others will prefer the standard version because it is standard and live with the resulting bugs. In addition, people will invent arguments why the standard version is best ( otherwise the standard committee wouldn t have chosen it ) to add to the confusion. 4 Iterator Traits The solution above is the minimal one that makes count() work as originally intended. However, the notion of an iterator trait is obviously general, and anyone defining one would provide the usual set of typedefs rather than just distance_type: template <class Iterator> struct iterator_trait typedef Iterator::distance_type distance_type; typedef Iterator::value_type value_type; template <class T> struct iterator_trait<t*> typedef ptrdiff_t distance_type; Should iterator_trait be documented as part of the library or treated as an implementation detail known only to implementors and varying between implementations? I propose iterator_trait as part of the library because something like that is the only way count can be done right. Given that iterator_trait must exist in every implementation and given that it is the basis for treating userdefined and pointer types equivalently in generic programs, it ought to be the same in every implementation.

- 3-5 Iterator Simplification using Iterator Traits The notion of iterator_traits allows a great simplification in the current mechanism for inferring types from iterators. In particular, 24 can be shortened by a few pages! The current iterator category mechanism gains type information through overload resolution. Given suitable definitions of iterator_trait and iterators this deduction can be done as partial specialization and the distance, value, and category types can be made available as types rather than as values. This is clearly a far more useful form for type information. To make category information available for use in iterator traits, the basic iterators ( 24.2.2) should present their category as a typedef. For example: template<class T, class Distance=ptrdiff_t> struct input_iterator typedef Distance distance_type; typedef input_iterator_tag iterator_category; template<class Iterator> struct iterator_trait typedef Iterator::distance_type distance_type; typedef Iterator::value_type value_type; typedef Iterator::iterator_category iterator_category; Given these definitions, iterator_trait provides all of the information currently provided by iterator_category(), value_type(), and distance_type(); these functions are thus redundant, and can be eliminated. Since iterator_trait<iterator>::value_type is a typedef, we can define value_type to be the return type of Iterator::operator* rather than a pointer to that type. At present, value_type(iterator) returns a pointer. Since it is possible to declare variables of type iterator_trait<iterator>::value_type directly, it is much rarer for auxiliary functions to be necessary. In particular, auxiliary functions are needed only when discriminating between different iterator categories. A further simplification is now possible: since input_iterator, output_iterator, forward_iterator, bidirectional_iterator, and random_access_iterator are identical except for the iterator_category typedefs, are eliminated in favor of a single template: template<class Category, class T, class Distance=ptrdiff_t> struct iterator typedef Distance distance_type; typedef Category iterator_category; Thus, iterator_traits provide a simpler, smaller, and more versatile mechanism for dealing with iterator categories. 6 Working Paper Changes Replace 25.1.6 [lib.alg.count] by template<class InputIterator, class T> iterator_trait<inputiterator>::distance_type count(inputiterator first, InputIterator last, const T& value); template<class InputIterator, class Predicate> iterator_trait<inputiterator>::distance_type count_if(inputiterator first, InputIterator last, Predicate pred); Requires: Type T is EqualityComparable (_lib.equalitycomparable_). Effects: Returns the number of iterators i in the range [first,last) for which the following corresponding conditions hold: *i==value, pred(*i)==true. Complexity: Exactly last-first applications of the corresponding predicate.

- 4 - In 17.3.1.1 [lib.contents], remove bidirectional_iterator, forward_iterator, input_iterator, and random_access_iterator from Table 4, and add iterator and iterator_trait. Remove distance_type, iterator_category, and value_type from Table 6. Remove output_iterator from Table 8. Remove iterator_category from Table 10. In 20.4 [lib.memory] change the comment For output_iterator to read For iterator. In 20.4.2 [lib.storage.iterator] change the declaration of raw_storage_iterator so that it is derived from iterator<output_iterator_tag,void,void>. This proposal involves many changes to 24. However, the vast majority are deletions as opposed to additions. To ease the job of the editor, we supply the complete revised text for 24.1.6 rather than listing the minor changes individually; in this case also, the replacement text is noticeably shorter than the current text. Replace 24.1.6 with: To implement algorithms only in terms of iterators, it is often necessary to infer both the value type and the distance type from the iterator. To enable this task it is required that for an iterator of type Iterator the types iterator_trait<iterator>::distance_type, iterator_trait<iterator>::value_type, and iterator_trait<iterator>::iterator_category be defined as the iterator s distance type, value type, and iterator category. In the case of an output iterator, iterator_trait<iterator>::distance_type and iterator_trait<iterator>::value_type are defined as void. [Example: to implement a generic reverse function, a C++ program can do the following: template<class BidirectionalIterator> void reverse(bidirectionaliterator first, BidirectionalIterator last) iterator_trait<bidirectionaliterator>::distance_type n = distance(first, last); --n; while(n > 0) iterator_trait<bidirectionaliterator>::value_type tmp = *first; *first++ = *--last; *last = tmp; n -= 2; --end example] The template iterator_trait<iterator> is defined as template<class Iterator> struct iterator_trait typedef Iterator::distance_type distance_type; typedef Iterator::value_type value_type; typedef Iterator::iterator_category iterator_category; It is specialized for pointers as template<class T> struct iterator_trait<t*> typedef ptrdiff_t distance_type; typedef random_access_iterator_tag iterator_category; [Note: If there is an additional pointer type far such that the difference of two far pointers is of type long, an implementation may define

- 5 - --end note] template<class T> struct iterator_trait<t far*> typedef long distance_type; typedef random_access_iterator_tag iterator_category; It is often desirable for a template function to find out what is the most specific category of its iterator argument, so that the function can select the most efficient algorithm at compile time. To facilitate this, the library introduces category tag classes which are used as compile time tags for algorithm selection. They are: input_iterator_tag, output_iterator_tag, forward_iterator_tag, bidirectional_iterator_tag, and random_access_iterator_tag. For every iterator of type Iterator, iterator_trait<iterator>::iterator_category must be defined to be the most specific category tag that describes the iterator s behavior. [Example: For a program-defined iterator BinaryTreeIterator, it could be included into the bidirectional iterator category by specializing the iterator_trait template: template<class T> struct iterator_trait<binarytreeiterator<t> > typedef ptrdiff_t distance_type; typedef bidirectional_iterator_tag iterator_category; Typically, however, it would be easier to derive BinaryTreeIterator<T> from iterator<bidirectional_iterator_tag,t,ptrdiff_t>. --end example] [Example: If a template function evolve() is well defined for bidirectional iterators, but can be implemented more efficiently for random access iterators, then the implementation is as follows: --end example] template <class BidirectionalIterator> inline void evolve(bidirectionaliterator first, BidirectionalIterator last) evolve(first, last, iterator_trait<bidirectionaliterator>::iterator_category()); template <class BidirectionalIterator> void evolve(bidirectionaliterator first, BidirectionalIterator last, bidirectional_iterator_tag) //... more generic, but less efficient algorithm template <class RandomAccessIterator> void evolve(randomaccessiterator first, RandomAccessIterator last, random_access_iterator_tag) //... more efficient, but less generic algorithm [Example: If a C++ program wants to define a bidirectional iterator for some data structure containing double and such that it works on a large memory model of the implementation, it can do so with: class MyIterator : public iterator<bidirectional_iterator_tag, double, long> // code implementing ++, etc. Then there is no need to specialize the iterator_trait template. --end example]

- 6 - Header <iterator> synopsis lib.iterator.synopsis #include <cstddef> // for ptrdiff_t #include <iosfwd> // for istream, ostream #include <ios> // for ios_traits #include <streambuf> // for streambuf namespace std // subclause _lib.library.primitives_, primitives: struct input_iterator_tag struct output_iterator_tag struct forward_iterator_tag struct bidirectional_iterator_tag struct random_access_iterator_tag template<class Category, class T, class Distance=ptrdiff_t> struct iterator; template<class Iterator> struct iterator_trait; template<class T> struct iterator_trait<t*>; // subclause _lib.iterator.operations_, iterator operations: template <class InputIterator, class Distance> void advance(inputiterator& i, Distance n); template <class InputIterator> iterator_trait<inputiterator>::iterator_trait distance(inputiterator first, InputIterator last); // subclause _lib.predef.iterators_, predefined iterators: template <class BidirectionalIterator, class T, class Reference = T&, class Pointer = T*, class Distance = ptrdiff_t> class reverse_bidirectional_iterator : public iterator<bidirectional_iterator_tag,t,distance>; template <class BidirectionalIterator, class T, class Reference, class Pointer, class Distance> bool operator==( const reverse_bidirectional_iterator <BidirectionalIterator,T,Reference,Pointer,Distance>& x, const reverse_bidirectional_iterator <BidirectionalIterator,T,Reference,Pointer,Distance>& y); template <class RandomAccessIterator, class T, class Reference = T&, class Pointer = T*, class Distance = ptrdiff_t> class reverse_iterator : public iterator<random_access_iterator_tag,t,distance>; template <class RandomAccessIterator, class T, class Reference, class Pointer, class Distance> bool operator==( <RandomAccessIterator,T,Reference,Pointer,Distance>& x, <RandomAccessIterator,T,Reference,Pointer,Distance>& y);

- 7 - Rewrite 24.2.2 as follows: template <class RandomAccessIterator, class T, class Reference, class Pointer, class Distance> bool operator<( <RandomAccessIterator,T,Reference,Pointer,Distance>& x, <RandomAccessIterator,T,Reference,Pointer,Distance>& y); template <class RandomAccessIterator, class T, class Reference, class Pointer, class Distance> Distance operator-( <RandomAccessIterator,T,Reference,Pointer,Distance>& x, <RandomAccessIterator,T,Reference,Pointer,Distance>& y); template <class RandomAccessIterator, class T, class Reference, class Pointer, class Distance> reverse_iterator<randomaccessiterator,t,reference,pointer,distance> operator+( Distance n, <RandomAccessIterator,T,Reference,Pointer,Distance>& x); template <class Container> class back_insert_iterator; template <class Container> back_insert_iterator<container> back_inserter(container& x); template <class Container> class front_insert_iterator; template <class Container> front_insert_iterator<container> front_inserter(container& x); template <class Container> class insert_iterator; template <class Container, class Iterator> insert_iterator<container> inserter(container& x, Iterator i); // subclauses _lib.stream.iterators_, stream iterators: template <class T, class Distance = ptrdiff_t> class istream_iterator; template <class T, class Distance> bool operator==(const istream_iterator<t,distance>& x, const istream_iterator<t,distance>& y); template <class T> class ostream_iterator; template<class chart, class traits = ios_traits<chart>, class Distance = ptrdiff_t> class istreambuf_iterator; template <class chart, class traits = ios_traits<chart> > bool operator==(istreambuf_iterator<chart,traits>& a, istreambuf_iterator<chart,traits>& b); template <class chart, class traits = ios_traits<chart> > bool operator!=(istreambuf_iterator<chart,traits>& a, istreambuf_iterator<chart,traits>& b); template <class chart, class traits = ios_char_traits<chart> > class ostreambuf_iterator; template<class chart, class traits = ios_char_traits<chart> > bool operator==(ostreambuf_iterator<chart,traits>& a, ostreambuf_iterator<chart,traits>& b); template<class chart, class traits = ios_char_traits<chart> > bool operator!=(ostreambuf_iterator<chart,traits>& a, ostreambuf_iterator<chart,traits>& b);

- 8 - template<class Category, class T, class Distance> struct iterator typedef Distance distance_type; typedef Category iterator_category; Delete 24.2.3, 24.2.4, and 24.2.5. Replace the definition distance() in section 24.2.6 with template <class InputIterator> iterator_trait<inputiterator>::distance_type distance(inputiterator first, InputIterator last); Effects: Returns the number of times it takes to get from first to last. and delete the footnote in that section. In 24.3.1.1, add iterator_trait<bidirectionaliterator>::value_type& as a default for T, and eliminate the footnote that says that this is impossible! Change the declaration of reverse_bidirectional_iterator so that it is derived from iterator<bidirectional_iterator_tag,t,distance>. Make the same changes in 24.3.1.3: add iterator_trait<randomaccessiterator>::value_type& as a default for T, and change reverse_iterator so that it is derived from iterator<random_access_iterator_tag,t,distance>. In 24.3.2.1 [lib.back.insert.iterator], derive back_insert_iterator from iterator<output_iterator_tag,void,void> instead of from output_iterator. In 24.3.2.3 [lib.front.insert.iterator], make the same change: derive front_insert_iterator from iterator<output_iterator_tag,void,void>. In 24.3.2.5 [lib.insert.iterator], derive insert_iterator from iterator<output_iterator_tag,void,void>. In 24.4.1 [lib.istream.iterator], and 24.4.2 [lib.ostream.iterator], derive istream_iterator and ostream_iterator from, respectively, iterator<input_iterator_tag,t,distance> and iterator<output_iterator_tag,void,void>. In 24.4.3 [lib.istreambuf.iterator] change the declaration of istreambuf_iterator to (this also fixes a bug in the current draft not related to the new definition of iterator): namespace std template<class chart, class traits = ios_traits<chart>, class Distance = ptrdiff_t> : public iterator<input_iterator_tag, chart, Distance> class istreambuf_iterator // no chage from current WP In 24.4.4 [lib.ostreambuf.iterator], derive ostreambuf_iterator from iterator<output_iterator_tag,void,void>. Delete the definition of the iterator_category() function in 24.4.3.6, 24.4.4, and 24.4.4.3. 7 Caveat Note that although we believe this code to be correct, we have not tested it: we do not have access to a compiler that supports partial specialization.