Why does this require a const_cast?

8 hours ago 1
ARTICLE AD BOX

I've got a Chess class that encodes the states of the squares on the board as 4 bit integers. To abstract this away, I'm doing what I think is the standard trick of creating a class which emulates a reference, in order to enable the use of the call / indexing operator.

The problem is that the const PieceRef overload fails to compile. Using a const_cast works; however it doesn't make sense. The non const PieceRef overload requires no such shenanigans, and I was very strongly under the impression that const functions and objects don't imbue any constness on their respective member variables. I was under the impression that the implementer of the class could do whatever they wanted and that it was merely the case that const objects could only call const functions.

I have two questions, namely the afore mentioned why, and have I created undefined behavior? I don't think I've created undefined behavior, because, aside from the fact that the code works, this Stack Overflow answer says that const_cast is okay as long as the underlying object isn't const. That said, I want to be sure.

#include "Chess.h" #include <optional> #include <iostream> int main() { Chess::Piece piece = Chess::Piece(); piece.color = Chess::Piece::Color::White; piece.kind = Chess::Piece::Kind::Rook; Chess chess = Chess(); chess.board(3, 3) = piece; std::cout << (static_cast<std::optional<Chess::Piece>>(chess.board(3, 3))->kind == Chess::Piece::Kind::Rook) << std::endl; std::cout << "abc" << std::endl; } #pragma once #include <optional> #include <cstdint> #include <cstddef> class Chess { public: struct Piece { enum class Color { White, Black }; enum class Kind { King, Queen, Rook, Bishop, Knight, Pawn }; Color color; Kind kind; }; public: class PieceRef { private: uint32_t& m_array; private: const size_t m_index; public: explicit PieceRef(uint32_t& array, const size_t& index); public: PieceRef(const PieceRef& piece_ref); public: PieceRef& operator =(const std::optional<Piece>& piece); public: PieceRef& operator =(const PieceRef& piece_ref); public: operator std::optional<Piece>()const; }; class Board { private: uint32_t m_array[8]; public: PieceRef operator ()(const size_t& row, const size_t& col); public: const PieceRef operator ()(const size_t& row, const size_t& col)const; }; public: Board board; }; #include "Chess.cpp" #include <optional> #include <cstdint> #include <cstddef> Chess::PieceRef::PieceRef(uint32_t& array, const size_t& index): m_array(array), m_index(index) {} Chess::PieceRef::PieceRef(const PieceRef& piece_ref): m_array(piece_ref.m_array), m_index(piece_ref.m_index) {} Chess::PieceRef& Chess::PieceRef::operator =(const std::optional<Piece>& piece) { uint32_t key = !piece ? 12 : (6 * static_cast<uint32_t>(piece->color)) + static_cast<uint32_t>(piece->kind); m_array = m_array & ~(0b1111 << (4 * m_index)) | (key << (4 * m_index)); return *this; } Chess::PieceRef& Chess::PieceRef::operator =(const PieceRef& piece_ref) { m_array = m_array & ~(0b1111 << (4 * m_index)) | (piece_ref.m_array & (0b1111 << (4 * piece_ref.m_index))); return *this; } Chess::PieceRef::operator std::optional<Piece>() const { uint32_t key = m_array >> (4 * m_index) & 0b1111; if (key == 12) { return {}; } Piece piece = Piece(); { piece.color = static_cast<Piece::Color>(key / 6); piece.kind = static_cast<Piece::Kind>(key % 6); } return piece; } Chess::PieceRef Chess::Board::operator ()(const size_t& row, const size_t& col) { return PieceRef(m_array[row], col); } const Chess::PieceRef Chess::Board::operator ()(const size_t& row, const size_t& col) const { return PieceRef(const_cast<uint32_t&>(m_array[row]), col); }
Read Entire Article