So I'm making a bitmask class that stores a reference to an std::byte as a member and the index of the individual bit to allow accessing the value of that bit and also assigning to that bit. I also want it to be possible for the value of the std::byte passed to optionally be a const, and if it is a const, I want the class itself to be considered a const or at least make sure operations that may change the underlying value of the std::byte (such as assignment) do not work. However I don't see a way to implement it without copypasting code which I consider to be too complicated. Is there an easier way to get around this? This is my current code for the bitmask class:
class bitmask
{
public:
bitmask(std::byte &chunk, std::uint_fast8_t index) noexcept
: _chunk(chunk), _index(index){};
bitmask(bitmask const &source) = default;
operator bool() const noexcept
{
return static_cast<bool>((_chunk >> (7 - _index)) & std::byte{1});
}
bitmask &operator=(bool val) noexcept
{
_chunk = ((_chunk & ~(std::byte{1} << (7 - _index))) |
(std::byte{val} << (7 - _index)));
return *this;
}
private:
std::byte &_chunk;
std::uint_fast8_t const _index;
};
What I want is to basically make a variant of it where chunk is a const reference and the assignment operator doesn't exist, without copy-pasting existing code to avoid reptition.
PS: I don't mind using any C++ standard, including C++20, as long as it solves the problem elegantly.
One option is to turn bitmask
into a template and use SFINAE + type traits to alter the behavior:
// vvv defaults to non-const, change if desired
template<typename Chunk = std::byte>
class bitmask
{
static_assert(std::is_same_v<std::remove_const_t<Chunk>, std::byte>);
public:
bitmask(Chunk &chunk, std::uint_fast8_t index) noexcept
: _chunk(chunk), _index(index){};
bitmask(bitmask const &source) = default;
operator bool() const noexcept
{
return static_cast<bool>((_chunk >> (7 - _index)) & std::byte{1});
}
template<bool Enable = !std::is_const_v<Chunk>, typename = std::enable_if_t<Enable>>
bitmask &operator=(bool val) noexcept
{
_chunk = ((_chunk & ~(std::byte{1} << (7 - _index))) |
(std::byte{val} << (7 - _index)));
return *this;
}
private:
Chunk &_chunk;
std::uint_fast8_t const _index;
};
When using C++17 or newer, template arguments need not be supplied manually as class template argument deduction will infer Chunk
based on the argument passed to bitmask
's constructor. Earlier versions of C++ can use a make_bitmask
factory + type aliases to accomplish similar aesthetics, though unfortunately the const and non-const variants will necessarily have to be spelled out differently.
So, despite there being some really nice answers here, I didn't find any of them particularly elegant, so I decided to delve deeper and solve my own problem. Note that this solution isn't entirely mine, and was originally inspired by @ildjarn 's answer, so props to them as well.
This is how I ended up solving my problem
// Class to mask reference to individual bit
template <bool is_const = false>
class bitmask
{
public:
using ChunkType = std::conditional_t<is_const, std::byte const, std::byte>;
bitmask(ChunkType &chunk, std::uint_fast8_t index) noexcept
: _chunk(chunk), _index(index){};
bitmask(bitmask const &source) = default;
operator bool() const noexcept
{
return static_cast<bool>((_chunk >> (7 - _index)) & std::byte{1});
}
template <typename = std::enable_if_t<!is_const>>
bitmask &operator=(bool val) noexcept
{
_chunk = ((_chunk & ~(std::byte{1} << (7 - _index))) |
(std::byte{val} << (7 - _index)));
return *this;
}
private:
ChunkType &_chunk;
std::uint_fast8_t const _index;
};
bitmask(std::byte &, std::uint_fast8_t)->bitmask<false>;
bitmask(std::byte const &, std::uint_fast8_t)->bitmask<true>;
So basically, the class is a template now which takes a boolean value depending on whether the byte referenced to is a const or not, and I also added template argument deduction hints for the constructor so the constness is automatically deduced. I also made operator=
only work if is_const
is false
.
This is what pointers allow. Either completely constant or completely variable. So, a true-false statement could always be made.
A template class that deduces of being constant or not as well.
template<class T>
class overload {
public:
overload(T t): t(t) {
}
~overload() {}
T get() {
if(std::is_const<T>::value)
clog <<"const\t " <<t <<endl;
else if(! std::is_const<T>::value)
clog <<"variable\t " <<t <<endl;
return this->t;
}
T set(T t) {
this->t= t;
}
private:
T t;
};
class test {
public:
test(const int * const _t) : _t(_t) {}
test(int *t) : t(t), _t(NULL) {}
~test() {}
int get() { return *(this->t); }
void set(int *t) { this->t= t; }
const int * const _get() { return (this->_t); }
int __get( ) {
return (_t==NULL)?*t:*_t;
}
//void _set(const int * const _t) { this->_t= _t; }
private:
int *t;
const int *const _t;
};
int main(int argc, char*argv[]) {
int n;
const int m= 99;
n= 100;
overload<int> o(n);
overload<const int> _o(m);
::cout <<o.get() <<endl;
::cout <<_o.get() <<endl;
test t(&n), _t(&m);
::cout <<t.get() <<"\t" <<*_t._get() <<"\t" <<t.__get() <<"\t" <<_t.__get() <<endl;
return 0;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.