I have a base class for pieces
class piece;
and an array containing derived objects
piece* board[8][8];
Advantag
The "super ugly switch statement" is the correct technique. It isn't ugly. It's called functional programming.
Inheritance is completely the wrong technique. Each of the pieces moves in a different way, has a different graphic, and other properties. There's nothing common. Chess pieces are not abstract. They're a concrete collection of discrete objects.
You have to make something common by unification: creating what is called a sum type. In Ocaml:
type shape = Pawn | Rook | Knight | Bishop | Queen | King
type color = Black | White
type piece = shape * color
type pos = { row:int; col:int }
let check_white_move piece board from to = match piece with
| Pawn -> on_board to && (from.row = 2 && to.row = 4 or to.row = from.row + 1)
| ....
In C++ there is no proper sum type, you can use instead:
enum shape { pawn, rook, knight, bishop, queen, king};
..
bool check_white_move (..) { switch piece {
case pawn: ...
It's more clumsy. Complain to the C and C++ committees. But use the right concept. Sum types (discriminated unions, variants) are the way to unify a discrete set of concrete types. Classes and inheritance are used for representing abstractions and providing implementations thereof.
There's nothing abstract about chess. It's all about combinations. This is not a question of advantages and disadvantages of different techniques: it's about using the correct technique.
[BTW: yea, you can try boost variant though I can't recommend it for this application since the pieces have no associated data an enum is perfect]