Normally someone would just go and grab Boost's Function Output Iterator but I'm not allowed to use Boost at work. That said, I just want to use the copy function to traverse a collection, call a function on each item, take the output of that function, and finally push_back it onto another collection. I've written some code:
#include <iterator>
using std::iterator;
using std::output_iterator_tag;
template<typename Container, typename Function>
struct Back_Transform_Iterator : public iterator<output_iterator_tag,void,void,void,void>{
explicit Back_Transform_Iterator(Container &_container, const Function &_function)
: m_Container(_container),
m_Function(_function){}
Back_Transform_Iterator<Container,Function>& operator= (const typename Function::argument_type &value){
m_Container.push_back( m_Function( value ) );
return *this;
}
Back_Transform_Iterator<Container,Function>& operator* (){ return *this; }
Back_Transform_Iterator<Container,Function>& operator++ (){ return *this; }
Back_Transform_Iterator<Container,Function> operator++ (int){ return *this; }
typedef Container container_type;
private:
Container &m_Container;
Function m_Function;
};
template<typename C, typename F>
Back_Transform_Iterator<C,F> back_transform_inserter(C &_container, F &_unary_function){
return Back_Transform_Iterator<C,F>( _container, _unary_function );
}
but... I'm getting compilation problems. Fairly certain it's to do with the operator*() call. I have no idea how to effectively dereference the container's objects so that they object the function's effects. Error:
error C2582: 'operator =' function is unavailable in 'Back_Transform_Iterator<Container,Function>'
the items I'm iterating over are not mutable. Anyone know how to tackle this issue?
You can use transform instead:
transform(src.begin(), src.end(), back_inserter(container), func);
Note that const applied on a reference type is a no-op. So if argument_type is T&, then saying const argument_type does not yield back const T& but T& again. So if your source items are constant you would try to bind them to the non-const reference parameter of operator= in case that argument_type is a reference:
typedef int &intr;
const intr& a = 0; // fails - "const" is ignored!
Related
The classes std::back_insert_iterator, std::front_insert_iterator and std::insert_iterator cannot be copied. Both the assignment operator and the constructor do not allow copies. Also the properties container (and iter in std::insert_iterator) are protected.
I wonder why. I'd like to copy these into a lambda function to use later to populate the containers.
Something like this doesn't seem possible:
template<class OutIter>
std::function<void()>
generateFunction(OutIter out_iter) {
return
[out_iter] // not possible and a reference becomes dangling
() {
// write to outpit iterator.
}
}
I'm just wondering why? It would be handy to be able to use the iterators here. Now I either have to build my own inserter or work directly on the containers.
Or does anyone have another idea?
Edit:
It seems to be copyable. But if i use it as a lambda capture, i got a const version of the inserter.
This code does not compile:
#include <functional>
#include <vector>
#include <iterator>
template<class OutIter>
inline std::function<void(int)>
generateFunction(OutIter out_iter) {
return
[out_iter] (int value) {
*out_iter = value;
++out_iter;
};
}
int main() {
std::vector<int> v;
auto myFn = generateFunction(std::back_inserter(v));
myFn(1);
myFn(2);
}
Error is:
<source>:10:13: error: passing 'const std::back_insert_iterator<std::vector<int> >' as 'this' argument discards qualifiers [-fpermissive]
10 | *out_iter = value;
Solution: The Lambda has to be mutable:
template<class OutIter>
inline std::function<void(int)>
generateFunction(OutIter out_iter) {
return
[out_iter] (int value) mutable {
*out_iter = value;
++out_iter;
};
}
Many thanks for the helpful answers.
The classes std::back_insert_iterator, std::front_insert_iterator and std::insert_iterator cannot be copied. Both the assignment operator and the constructor do not allow copies.
Your assumption is wrong. All of the iterators mentioned must satisfy the LegacyOutputIterator named requirement which implies that they also satisfy the LegacyIterator named requirement which requires the iterator to be copyable
Gcc, clang and MSVC all consider the iterators to be copyable, see godbolt.
template <class Container>
class insert_iterator :
public iterator<output_iterator_tag,void,void,void,void>
{
protected:
Container* container;
typename Container::iterator iter;
public:
typedef Container container_type;
explicit insert_iterator (Container& x, typename Container::iterator i)
: container(&x), iter(i) {}
insert_iterator<Container>& operator= (typename Container::const_reference value)
{ iter=container->insert(iter,value); ++iter; return *this; }
insert_iterator<Container>& operator* ()
{ return *this; }
insert_iterator<Container>& operator++ ()
{ return *this; }
insert_iterator<Container>& operator++ (int)
{ return *this; }
};
I'm in the process of writing up an STL-like library for learning purposes. All of the collections extend a class called Iterable which contains wrapping functions for all of the functions found in <algorithm>. For example, it allows vec.each([](T t){...}); which I strongly prefer over the verbose std::for_each. The function giving me problems is count - I want to overload Iterable::count so it combines the behaviour of both std::count and std::count_if depending on the argument type but I'm running into a strange error.
Iterable.h
virtual int count(const T& value) const {
return std::count(begin(), end(), value);
}
virtual int count(std::function<bool(T&)> predicate) {
return std::count_if(begin(), end(), predicate);
}
virtual int count(std::function<bool(const T&)> predicate) const {
return std::count_if(begin(), end(), predicate);
}
main.cpp
Vector<int> vec; // extends Iterable
vec.add(0);
vec.add(1);
vec.count([](int i){ return i == 0; }); // compiles and works fine
vec.count(0); // error c2666: 3 overloads have similar conversions
I should note that changing the count_if wrapper function names to count_if does work and resolves the ambiguity, but I'd prefer to have them named count and also to figure out why there is ambiguity in the first place.
From what I interpret, the compiler is trying to make a new std::function using the template <class F> function(F f) ctor, then runs into the ambiguity. Is that the case? It seems odd since the line below fails to compile as well.
std::function<bool(int)> f(0); // error C2064: term does not evaluate to a function taking 1 arguments
Any insights or potential fixes are much appreciated.
Forgot to say; using visual studio 2012, nov 2012 ctp compiler
std::function<Sig> in the published C++11 standard without errata contains a constructor that thinks it can consume anything, as far as its signature is concerned. If you pass it things it cannot consume (things that are not callable), it fails to compile.
Overload resolution occurs earlier (based on shallower information) than the compile failure. It matches on signatures, not implementations.
A bug report and a fix was proposed, so some C++11 compilers can fix this, and all C++14 compilers must fix this.
VS2012 has limited SFINAE overload resolution capabilities. But one approach would look like:
template<class Sig, class=void>
struct is_filter_on : std::false_type{};
template<class F, class Arg>
struct is_filter_on< F(Arg),
typename std::enable_if<std::is_convertible<
typename std::result_of<F(Arg)>::type
,bool>::value>::type
> : std::true_type{};
which is an attempt at a traits class that tells you if F(Arg) is a bool-returning "filter" on values of type Arg.
template<class X>
size_t count(X&& x) const {
return count( std::forward<X>(x), is_filter_on< X&(T const&) >{} );
}
template<class X>
size_t count(X&& x) {
return count( std::forward<X>(x), is_filter_on< X&(T&) >{} );
}
template<class F>
size_t count(F&& f, std::true_type) const {
return std::count_if( begin(), end(), std::forward<F>(f) );
}
template<class F>
size_t count(F&& f, std::true_type) {
return std::count_if( begin(), end(), std::forward<F>(f) );
}
template<class X>
size_t count(X&& x, std::false_type) const {
return std::count( begin(), end(), std::forward<X>(x) );
}
template<class X>
size_t count(X&& x, std::false_type) {
return std::count( begin(), end(), std::forward<X>(x) );
}
but I have no idea of MSVC2012 will work with the above.
Here I use tag dispatching to pick which version of count I call. The traits class is_filter_on does a test to determine if the pseudo-expression F(Arg) is filter-like. If so, we dispatch to the std::count_if. Otherwise, we dispatch to the std::count version.
The problem is that 0 is ambiguous here, it can be interpreted as a null pointer or an int, which makes it match both the std::function constructor and the more general const T& value (both require a conversion).
If you don't want to change the interface, you can just create a very simple function template to deduce and dispatch the arguments.
C++11 version:
template<typename U>
int count(U&& value) const {
return count_impl(std::forward<U>(value));
}
This works because the function template type deduction rules don't have that ambiguity, they never treat 0 as a null pointer.
So your interface is now:
virtual int count_impl(const T& value) const {
return std::count(v.begin(), v.end(), value);
}
virtual int count_impl(std::function<bool(T&)> predicate) {
return std::count_if(v.begin(), v.end(), predicate);
}
virtual int count_impl(std::function<bool(const T&)> predicate) const {
return std::count_if(v.begin(), v.end(), predicate);
}
template<typename U>
int count(U&& value) const {
return count_impl(std::forward<U>(value));
}
And you can use it naturally:
int main(){
Vector<int> vec; // extends Iterable
vec.count([](int i){ return i == 0; }); // compiles and works fine
vec.count(0); // no problem, calls virtual int count_impl(const T& value) const
}
In responding to this question on CodeReview, I was thinking about how one might write a template function to indicate const-ness of a contained object.
To be specific, consider this templated function
#include <iostream>
#include <numeric>
#include <vector>
template <class It>
typename std::iterator_traits<It>::value_type average(It begin, It end) {
typedef typename std::iterator_traits<It>::value_type real;
real sum = real();
unsigned count = 0;
for ( ; begin != end; ++begin, ++count)
sum += *begin;
return sum/count;
}
int main()
{
std::vector<double> v(1000);
std::iota(v.begin(), v.end(), 42);
double avg = average(v.cbegin(), v.cend());
std::cout << "avg = " << avg << '\n';
}
It takes an iterator and calculates an average based on the contained numbers, but it is guaranteed not to modify the vector through the passed iterators. How does one convey this to a user of the template?
Note that declaring it like this:
template <class It>
typename std::iterator_traits<It>::value_type average(const It begin,
const It end)
doesn't work because it's not the iterator, but the thing the iterator points to, that's const. Do I have to wait for concepts to be standardized?
Note that I don't want to require const iterators, but instead to indicate that they may be safely used here. That is, rather than restricting the caller, I want to convey a promise that my code is making: "I will not modify your underlying data."
template <class ConstIt>
It's that simple. There's nothing to be enforced on the caller side here, as a non-const iterator is also usable for const access, so it's just API documentation, and that's what your choice of parameter identifier is - API documentation.
That does lead on to the question of enforcement on the callee/function side - so it can't be pretending it will only use the iterator for const access then modify elements anyway. Should you care about that, you could accept the parameter using some identifier making it clear it wasn't meant to be used everywhere throughout the function, then create a const_iterator version with a more convenient identifier. That could be tricky as in general you don't know if the iterator type is a member of a container, let alone what that container type is and whether it has a const_iterator too, so some manner of Concepts would indeed be ideal - fingers crossed for C++14. Meanwhile:
have your caller tell you the container type,
write your own traits, OR
write a simple wrapper class that holds an iterator and ensures only const access to the referenced data escapes the interface
This last wrapper approach is illustrated below (not all of the iterator API is implemented so flesh out as needed):
template <typename Iterator>
class const_iterator
{
public:
typedef Iterator iterator_type;
typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
// note: trying to add const to ...:reference or ..:pointer doesn't work,
// as it's like saying T* const rather than T const* aka const T*.
typedef const typename std::iterator_traits<Iterator>::value_type& reference;
typedef const typename std::iterator_traits<Iterator>::value_type* pointer;
const_iterator(const Iterator& i) : i_(i) { }
reference operator*() const { return *i_; }
pointer operator->() const { return i_; }
bool operator==(const const_iterator& rhs) const { return i_ == rhs.i_; }
bool operator!=(const const_iterator& rhs) const { return i_ != rhs.i_; }
const_iterator& operator++() { ++i_; return *this; }
const_iterator operator++(int) const { Iterator i = i_; ++i_; return i; }
private:
Iterator i_;
};
Sample usage:
template <typename Const_Iterator>
void f(const Const_Iterator& b__, const Const_Iterator& e__)
{
const_iterator<Const_Iterator> b{b__}, e{e__}; // make a really-const iterator
// *b = 2; // if uncommented, compile-time error....
for ( ; b != e; ++b)
std::cout << *b << '\n';
}
See it running at ideone.com here.
You may add some traits to see if the iterator is a const_iterator:
template <typename IT>
using is_const_iterator =
std::is_const<typename std::remove_reference<typename std::iterator_traits<IT>::reference>::type>;
And then use something like:
template <typename IT>
typename
std::enable_if<is_const_iterator<IT>::value,
typename std::iterator_traits<It>::value_type
>::type average(It begin, It end);
But this will avoid the use of iterator which are convertible to const_iterator.
So it will be better to restrict iterator when const is forbidden (as in std::sort)
It takes an iterator and calculates an average based on the contained numbers, but it is guaranteed not to modify the vector through the passed iterators. How does one convey this to a user of the template?
You could use SFINAE to disable the template when non-const iterators are passed, but that would be an unnecessary limitation.
Another way is to accept ranges instead of iterators. This way you could write:
template <class Range>
typename Range::value_type average(Range const& range);
The user can pass a container or iterator range in there.
You could try always dereferencing the iterator through some function deref().
template <typename It>
typename ::std::remove_reference<typename ::std::iterator_traits<It>::reference>::type const&
deref(It it)
{
return *it;
}
Which would guarantee the underlying value will not be modified.
In current C++, the class ostream_iterator was designed like the following:
// excerpted from the standard C++
template<class T, ...>
class ostream_iterator
{
public:
ostream_iterator(ostream_type&);
...
ostream_iterator<T,...>& operator =(const T&);
...
};
To me, this design is suboptimal. Because the user must specify the type T when declaring an ostream_iterator like this: ostream_iterator<int> oi(cout); In fact, cout can take any type of object as its argument, rather than only one type. This is an obvious restriction.
// Below is my own version
// doesn't need any template parameter here
class ostream_iterator
{
public:
ostream_iterator(ostream_type&);
...
// define a template member function which can take any type of argument and output it
template<class T>
ostream_iterator<T,...>& operator =(const T&);
...
};
Now, we can use it as follows:
ostream_iterator oi(cout);
I think it is more generic and more elegant than
ostream_iterator<int> oi(cout);
Am I right?
The simple answer is that iterator have associated types and ostream_iterator conceptually violates the concept of an iterator by requiring a value_type even when it is not necessary. (This is basically #pts's answer)
What you are proposing is related to the idea behind the new "transparent operators", such as the new std::plus<void>. Which consist in having a special instantiation whose member function has a delayed type deduction.
It is also backward compatible because void is not a useful instantiation to begin with. Moreover the void parameter is also the default. For example template<T = void> struct std::plus{...} is the new declaration.
A possible implementation of a transparent ostream_iterator
Going back of std::ostream_iterator, an important test is whether we want to make it work with std::copy as std::ostream_iterator is usually used:
std::vector<int> v = {...};
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
The technology for a transparent std::ostream_iterator is not there yet, because this fails:
std::copy(v.begin(), v.end(), std::ostream_iterator<void>(std::cout, " "));
To make this work, one can explicitly define the void instance. (This completes #CashCow 's answer)
#include<iterator>
namespace std{
template<>
struct ostream_iterator<void> :
std::iterator<std::output_iterator_tag, void, void, void, void>
{
ostream_iterator(std::ostream& os, std::string delim) :
os_(os), delim_(delim)
{}
std::ostream& os_;
std::string delim_;
template<class T> ostream_iterator& operator=(T const& t){
os_ << t << delim_;
return *this;
}
ostream_iterator& operator*(){return *this;}
ostream_iterator& operator++(){return *this;}
ostream_iterator& operator++(int){return *this;}
};
}
Now this works:
std::copy(v.begin(), v.end(), std::ostream_iterator<void>(std::cout, " "));
Moreover, if we convince the standard committee to have a default void parameter (as they did with with std::plus):
template<class T = void, ...> struct ostream_iterator{...}, we could go a step further and omit the parameter altogether:
std::copy(v.begin(), v.end(), std::ostream_iterator<>(std::cout, " "));
The root of the problem and a possible way out
Finally, in my opinion the problem might also be conceptual, in STL one expects an iterator to have a definite value_type associated even if it is not necessary like here. In some sense ostream_iterator violates some concepts of what is an iterator.
So there are two things that are conceptually wrong in this usage: 1) when one copies one expects to know the type of the source (container value_type) and target types 2) one is not copying anything in the first place!. In my opinion there is a double design mistake in this typical usage. There should be a std::send that works with a template shift << operators directly, instead of making = redirect to << as ostream_iterator does.
std::send(v.begin(), v.end(), std::cout); // hypothetical syntax
std::send(v.begin(), v.end(), std::ostream_receiver(std::cout, " ")); // hypothetical syntax
std::send(v.begin(), v.end(), 'some ostream_filter'); // hypothetical syntax
(The last argument should fulfill some kind of Sink concept).
** Using std::accumulate instead and a possible implementation of
std::send **
From a conceptual point of view, sending objects to a stream is more of an "accumulate" operation than a copy operator, so in principle std::accumulate should be a more suitable candidate, besides we don't need "target" iterators for it.
The problem is that std::accumulate wants to make copies of every object that is being accumulated, so this doesn't work:
std::accumulate(e.begin(), e.end(), std::cout,
[](auto& sink, auto const& e){return sink << e;}
); // error std::cout is not copiable
To make it work we need to do some reference_wrapper magic:
std::accumulate(e.begin(), e.end(), std::ref(std::cout),
[](auto& sink, auto const& e){return std::ref(sink.get() << e);}
);
Finally, the code can be simplified by having the equivalent of std::plus for the shift operator, in modern C++ this should look like this IM:
namespace std{
template<class Sink = void, class T = void>
struct put_to{
std::string delim_;
using sink_type = Sink;
using input_type = T;
Sink& operator()(Sink& s, T const& t) const{
return s << t << delim_;
}
};
template<>
struct put_to<void, void>{
std::string delim_;
template<class Sink, class T>
Sink& operator()(Sink& s, T const& t){
return s << t;
}
template<class Sink, class T>
std::reference_wrapper<Sink> operator()(std::reference_wrapper<Sink> s, T const& t){
return s.get() << t << delim_;
}
};
}
Which can be used as:
std::accumulate(e.begin(), e.end(), std::ref(std::cout), std::put_to<>{", "});
Finally we can define:
namespace std{
template<class InputIterator, class Sink>
Sink& send(InputIterator it1, InputIterator it2, Sink& s, std::string delim = ""){
return std::accumulate(it1, it2, std::ref(s), std::put_to<>{delim});
}
}
Which can be used as
std::send(e.begin(), e.end(), std::cout, ", ");
Finally, there is no dilemma about the type of any output_iterator here.
It appears you could be right.
Let's see if we can construct an ostream_iterator that does not need a template argument.
The iterator works by copying values into it, so *iter = x; ++iter;
The iterator cheats by making operator* return itself and ++iter also returning itself without changing any state. The "magic" is in the operator= which performs the output.
The "cout" must be a class member of type ostream*. It needs to be a pointer as iterators must be assignable, thus we assign the member (call it os) to the address of the stream passed in.
So we would overload operator= this way:
template< typename T >
our_ostream_iterator& operator=( const T& t )
{
(*os) << t;
if( delim )
(*os) << delim;
return *this;
}
Note that the templatised operator= should not oveload operator=(our_ostream_iterator const&) which is more specialised than the template.
You would still want a template on the element type so we will call that our_basic_ostream_iterator
ostream_iterator would still remain a template class on its element type. Thus:
template< typename E, typename TR=char_traits<E> >
class our_basic_ostream_iterator : public std::iterator< /*traits here*/ >
{
public:
typedef E element_type;
typedef TR traits_type;
typedef basic_ostream< E, TR > stream_type;
private:
stream_type * os;
const E* delim;
public:
our_basic_ostream_iterator( stream_type s, const E* d = nullptr ) :
os( &s ), delim( d )
{
}
our_basic_ostream_iterator& operator++() { return *this; }
our_basic_ostream_iterator operator++(int) { return *this; }
our_basic_ostream_iterator& operator*() { return *this; }
template< typename T >
our_basic_ostream_iterator& operator=( const T& t ); // as above
};
and then of course
typedef our_basic_ostream_iterator<char> our_ostream_iterator;
typedef our_basic_ostream_iterator<wchar_t> our_wostream_iterator;
The drawback of all of this though is that the above does not conform to all the properties of iterators such that it could be passed to any algorithm / class that requires a forward iterator. Why? Because such an algorithm should be able to invoke iterator_traits to extract the element type and the class above does not contain an element type.
It would lead to compile-time errors in the algorithm that is using your iterator and would potentially be hard to track down the reason why.
I think the reason is that it has other members also. Obviously the entire set of member functions need to be consistent in their behavior for a given set of T and other template arguments.
There's danger in operator < being instantiated for a set of template arguments which is different from what is used to instantiate operator * or operator++
Hence, the individual methods are not template themselves and rather the entire class is a template so ensure uniform T and other template arguments.
Yes, you are right. It would be more flexible as you suggest. However, the way it's designed fits more closely to how STL uses iterators: one iterator type for data type (T).
I need someone who explain me this code, line by line. I specially don't understand this line:
operator std::map<T, U>()
Thank you very much.
template <typename T, typename U>
class create_map
{
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
operator std::map<T, U>()
{
return m_map;
}
This is user-defined conversion function.
That means, you can write this:
//obj is an object of type create_map
create_map<int,std::string> obj(1,"Nawaz");
//obj implicitly converts into std::map type!
std::map<int,std::string> map_inst=obj;
See this topic to know more about user-defined conversion function:
User Defined Conversions in C++
You can see this as well: Implicit conversion sequences (C++ only)
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
This actually overloads the operator(), which internally inserts or updates (key,val) pair into the m_map; just see the function definition as to what it does.
So using this function you can write code like this,
obj(2,"Sarfaraz"); //this inserts the pair into the underlying m_map;
I would also suggest you to explore std::map a bit more, especially the overloaded operator[] in std::map.
Code:
template <typename T, typename U>
class create_map
{
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
The purpose of this code is to be able to specify a map with specific key/value pairs, by chaining calls to operator(), like
create_map<int, std::string>( 1, "blah" )( 2, "hah" )( 3, "doh" )
Since the class does not have a default constructor, there's no way to use it to create an empty map. That may be by design. Or it may be a design error.
The
operator std::map<T, U>()
{
return m_map;
}
defines a conversion to std::map<T, U>, which is the end result of it all.
It can be invoked implicitly wherever a create_map is specified and a std::map is expected, such as using a create_map expression as argument in a function call.
However, it can be pretty inefficient since it copies the map. The compiler may well optimize away the copying. But it’s ungood to needlessly rely on Quality Of Implementation (although sometimes that is the best that one can do).
So it should instead be
operator std::map<T, U> const& () const
{
return m_map;
}
The const at the end there allows a create_map to be declared as const and used.
With this conversion to reference there is the same problem as with using reference arguments, namely a theoretical possibility of aliasing, where code that retains a reference to const is not prepared to deal with changes of the referred object. But in practice it’s not a problem. For example, as formal argument one just writes std::string const& s (instead of just std::string s) as a matter of course, and very few if any errors result from that – I’ve never experienced any problem.
Cheers & hth.,
There's not much to understand about it. operator std::map<T, U>() overrides the class' conversion operator (which takes no parameters) to provide an object instance of type std::map<T, U>. std::map is a STL standard class for associative key->value storage. In your case it maps from keys of type T to values of type U. T and U have been undefined so far (you wrote template class, but where are the template parameters?)
The conversion operator allows to use the class instance in place of the type the operator provides conversion for, like this.
class foo {
operator char const *() {
return "foo instance as char const *";
}
};
// ...
void bar(foo &f)
{
// here the class instance is used as if it were a char const *
cout << f << endl;
}
The line
operator std::map<T, U>()
defines a function that will be called when your create_map object is used like an std::map somewhere in the code.
An easier example would be:
class A
{
public:
operator int()
{
return 3;
}
};
int main()
{
A a;
cout << a << endl;
}
Now the computer finds out that it doesn't know how to print variable a, but it knows how to convert it to an int and then print it. So "3" gets printed.