简体   繁体   中英

C++ class override for const and non-const reference member

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM