4

I write a print template function which prints "True" or "False" for bool type value.

I tried to apply it to the for_each algorithm but it does not work.

The output is still 0 or 1. It seems the template specialization does not work in for_each. How can this happen?

How can I make the code work as expected?

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <ctime>

using namespace std;

template <typename T>
void _print(const T& i)
{
    cout<<i<<endl;
}
template <>
void _print<bool>(const bool& i)
{
    if (i == 0)
        cout<<"False"<<endl;
    else
        cout<<"True"<<endl;
}

class print
{
public:
  template <typename T>
  void operator()(const T& val) const { _print(val); };
};

int main()
{
  auto even = [&](int i)->bool{return (i%2==0);};
  srand(time(NULL));
  vector <int> test(3);
  generate(test.begin(),test.end(),[]()->int{return rand()%100;});
  for_each(test.begin(),test.end(),print());
  vector <bool> flag(3);
  transform(test.begin(),test.end(),flag.begin(),even);
  for_each(flag.begin(),flag.end(),print());

  return 0;

}

The output is:

34
23
3
1
0
0

but I expect:

34
23
3
True
False
False
Jarod42
  • 203,559
  • 14
  • 181
  • 302
iceiceice
  • 187
  • 1
  • 6
  • It's not an error message, read the question more closely. – japreiss Sep 11 '14 at 15:45
  • Is there a really good reason you need this instead of just `std::cout << std::boolalpha << yourbool;`? – Jerry Coffin Sep 11 '14 at 15:46
  • I'm so sick of SO answers like Jerry's. Did you ever think, maybe, the real situation is more complex and this is an [SSCCE](http://sscce.org/)? Can't we give users the benefit of the doubt and assume they *actually want an answer to the question* instead of a suggestion to do something else? This is an interesting question about template specialization rules in C++. Who cares if there's a better way to implement the toy example? – japreiss Sep 11 '14 at 15:48
  • @japreiss, yes we can, when there is a K letter in user's reputation score :) – Basilevs Sep 11 '14 at 15:50
  • The problem is that `operator[]` and likewise of `std::vector[]` [do no return a `bool&`](http://en.cppreference.com/w/cpp/container/vector_bool). – 5gon12eder Sep 11 '14 at 15:51
  • @Basilevs Point taken, but I wish more users were aware of your rule because I still get these answers at 4.5k. – japreiss Sep 11 '14 at 15:56
  • Suggested Reading:http://stackoverflow.com/a/17794965/1870232 – P0W Sep 11 '14 at 15:56
  • 1
    @japreiss: 1) It was a comment, not an answer. 2) It was in the form of a question, and honestly was intended as a question, not sarcasm. Can't we give users the benefit of the doubt, and assume they actually want to accomplish what they seem to be trying to accomplish and help them out instead of answering a question in a way that happens to interest us instead of helping them? And while high rep may be bad, I try not to hold it against people, and try to help out when I can even if their rep is much higher than any sane person's could possibly be. – Jerry Coffin Sep 13 '14 at 00:57

2 Answers2

10

The evil part of std::vector<bool>.
vector<bool>::reference is not bool as you may expect but a proxy.

You may add this extra overload (or specialization if you prefer):

void _print(const vector<bool>::reference& i)
{
    _print<bool>(i);
}

Live example

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • So it is. I am trying to introduce functional programming ideas into C++ and use algorithms like high-order functions. Are all the containers have such wrappers like vector does? For example, vector::reference is not int either? – iceiceice Sep 11 '14 at 15:59
  • @iceiceice: `vector` is the only standard "container" that doesn't behave like a container. All the others (other containers, and other specialisations of `vector`) define `reference` to be a regular reference to the value type, so will work with your example code. – Mike Seymour Sep 11 '14 at 16:01
  • `std::vector` is one of the weirdest decisions ever. how could they not realize it's a bad idea? Why didn't they just make a `packed_bool_vector` type? – japreiss Sep 11 '14 at 16:10
4

The trouble is that vector<bool> is not a container of bool. Instead, it packs multiple "boolean" values into each byte of its storage, so that it's not possible to obtain a reference to any bool value. Instead, dereferencing an iterator gives a "proxy" object of type vector<bool>::reference, which behaves like a reference to bool in some, but not all, circumstances.

In this case, that's not good enough; your specialisation of _print expects a genuine const bool& reference, but _print instead passes an object of the reference type, from which the generic template is deduced.

You could support vector<bool> as a special case by adding an overload for this type:

void _print(std::vector<bool>::reference r) {_print(bool(r));}

(By the way, you shouldn't be using reserved names like _print)

Community
  • 1
  • 1
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644