
Le 22/03/2017 à 23:40, Andrey Semashev via Boost a écrit :
On 03/22/17 19:02, Roman Orlov via Boost wrote:
On 21.03.2017 21:23, Steven Watanabe via Boost wrote:
what's wrong with struct animal { bool eats_meat : 1; bool eats_grass : 1; bool has_tail : 1; };
There are several reasons I don't like that approach
While declaring a variable of type 'animal' you should always keep in mind to initialize it with empty initializer animal a1; // bit fields are not initialized animal a2{}; // now it's ok, bits are set to zeros
When you want to initialize some fields (eats_grass, has_tail) you have to write several lines of code animal a1{}; a1.eats_grass = 1; a1.has_tail = 1;
Of course, it can be done in one line with initializer list animal a1{0, 1, 1};
But we don't write a code ones, we are about to support it for a long time. One day we decided to add a new bit field to structure struct animal { bool can_fly : 1; // yeah, somebody puts it on the first position bool eats_meat : 1; bool eats_grass : 1; bool has_tail : 1; };
What will happen with all of list initializers? There will be bugs we can't detect at compile time. animal a1{0, 1, 1}; // now it doesn't work properly
We have to find all of such initializers and rewrite them animal a1{0, 0, 1, 1};
Frankly, all the above problems are solved with constructors.
The only real problem I see with the struct approach is that it's lacking expressiveness. From this declaration:
animal a1{0, 1, 1};
it is difficult to immediately say what the value of has_tail will be. It would be better if we had named field initialization, like in C, but alas.
Usually, I solve this problem with std::bitset with an enum providing names for the flags.
enum animal_traits { eats_meat, eats_grass, has_tail, _count }; typedef std::bitset< animal_traits::_count > animal;
animal a1; // all traits are false a1[eats_grass] = 1; a1[has_tail] = 1;
I think, this provides the necessary expressiveness and performance. I guess, your proposal provides better type safety than this solution, but I'm not sure the improvement is significant enough to outweigh the more complicated syntax. For example, one could write a slightly more complicated version of the above:
enum animal_traits { eats_meat, eats_grass, has_tail, _count };
template< typename Enum, unsigned int Count > class typed_bitset : public std::bitset< Count > { public: bool operator[] (Enum idx) const { return std::bitset< Count >::operator[](idx); } };
typedef typed_bitset< animal_traits, animal_traits::_count > animal;
animal a1; // all traits are false a1[eats_grass] = 1; a1[has_tail] = 1; a1[10] = 1; // error
I suspect it would also be faster to compile. compile time for ordinal sets would suffer a little bit as we need to associate at compile time each enumerator to its position (this could be
I have an ordinal_set<Ordinal> that is type safe.
An enum as the previous one canbe see as an Ordinal and so
enum animal_traits
{
eats_meat, eats_grass, has_tail, _count
};
using animal_traits_set = ordinal_set