1

How does this code not compile? Why can bs[1] not be deduced to bool?

Is there a generic way to solve this problem?

#include <iostream>
#include <string>
#include <bitset>
using namespace std;

template<typename T> struct StringConverter{};
template<> struct StringConverter<bool>{
    std::string operator()(const bool &i){ return i?"true":"false"; }   
};

template<typename T> std::string to_string(const T &val){
    return StringConverter<T>()(val);
}

int main(){
    // this does not compile
    std::bitset<10> bs;
    std::cout << to_string(bs[0]) << std::endl;

    // this does
    const std::bitset<10> bs_const;
    std::cout << to_string(bs_const[0]) << std::endl;
}

Compiler Error:

main.cpp:12:12: error: type 'StringConverter<std::bitset<10>::reference>' does not provide a call operator
    return StringConverter<T>()(val);
           ^~~~~~~~~~~~~~~~~~~~
main.cpp:18:18: note: in instantiation of function template specialization 'to_string<std::bitset<10>::reference>' requested here
    std::cout << to_string(bs[0]) << std::endl;
                 ^
1 error generated.
Finomnis
  • 18,094
  • 1
  • 20
  • 27

2 Answers2

3

the non-const bitset::operator[] returns a proxy object rather than a bool (this has to be done because that proxy can be used to change the bit value). const bitset::operator[] however just returns a bool (not a reference, just a plain bool) so it matches for the StringConverter[

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
1

If you check the declaration of operator[], you'll notice it has two overloads - the const one, which returns bool and is used in your second example, and the non-const, which returns the object of type std::bitset::reference.

The latter is used for bit field modification, and it absolutely cannot be a bool& since it has to address a specific bit. The problem you ran into is quite common for these proxy return types (this is where I should mention vector<bool>).

As a possible solution you can use the fact that std::bitset::reference is convertible to bool (and is not convertible to any other conceivable type that you might use for your StringConverter specializations).

Ap31
  • 3,244
  • 1
  • 18
  • 25
  • The problem is, that this would make this function the default fallback function, because a lot of things are convertible to bools. For example, if I have another specialisation that can print pointers, and enable it with std::is_pointer, and enable the boolean one with std::is_convertible, I'm not sure which one a pointer would choose ... – Finomnis Dec 09 '17 at 23:53
  • well, you can explicitly disable it for pointers and numerical types, and hopefully any other specialization would be an exact match. Of course, disabling it by hand for any significant amount of cases (besides pointers/numbers) would be quite ugly. On the bright side, it will work with `vector::reference` and alike out of the box. – Ap31 Dec 10 '17 at 12:41