Conways Game of Life using unordered set of coordinates

Previously I had been using an array of bool(dead/live) to represent cells. I looked on code review to see how other people have implemented Conway's Game of Life. Someone in the comments of a post suggested using an unordered set to only store live cells. This sounds like it would be more efficient, because only storing live cells means that the program only has to check live cells and their neighbours. I tried it, but I am not sure if the program is more efficient the way I have implemented it. Any suggestions are appreciated.

Grid Class

#ifndef UTILITIES_H_INCLUDED
#define UTILITIES_H_INCLUDED

#include<iostream>
#include<unordered_set>

namespace ConwaysGameOfLife
{
struct Grid
{
private:
std::size_t cols{25};
std::size_t rows{25};

int top{-cols};
int bottom{cols};
const int left{-1};
const int right{1};
const int middle{0};

using set_of = std::unordered_set<std::size_t>;

set_of cells_at_t{55+76,81+76,104+76,105+76,106+76,205+76,206+76,230+76,231+76};
set_of cells_at_t_minus_one;

std::unordered_set<int> neighbors
{
top+left,
top,
top+right,
left,
right,
bottom+left,
bottom,
bottom+right
};
void rule(const std::size_t& position)
{
std::size_t live_neighbors{0};

for(const auto& neighbor : neighbors)
//Total Neighbors
{
if(cells_at_t.find(position + neighbor) != dead)
{
live_neighbors++;
}
}
if(cells_at_t.find(position) != dead && live_neighbors < 2)
//Underpopulation
{
cells_at_t_minus_one.erase(position);
}
else if(cells_at_t.find(position) != dead && (live_neighbors == 2 || live_neighbors == 3))
//Aging of a Cell
{
cells_at_t_minus_one.insert(position);
}
else if(cells_at_t.find(position) == dead && live_neighbors == 3)
//Birth of a Cell
{
cells_at_t_minus_one.insert(position);
}
else if(cells_at_t.find(position) != dead && live_neighbors > 3)
//Overpopulation
{
cells_at_t_minus_one.erase(position);
}
}
public:
void apply()
{
set_of cells;
for(const auto& position : cells_at_t)
{
if(cells.insert(position).second);
{
rule(position);
}
for(const auto& neighbor : neighbors)
{
if(cells.insert(position + neighbor).second);
{
rule(position + neighbor);
}
}
}
cells_at_t = cells_at_t_minus_one;
}
void display()
{
for(int r{0}; r < rows; ++r)
{
for(int c{0}; c < cols; ++c)
{
if(cells_at_t.find(r * cols + c) != dead)
std::cout << '0';
else
std::cout << ' ';
}
std::cout << std::endl;
}
std::cout << std::endl;
}
Grid() {}
~Grid() {}
};
}

#endif // UTILITIES_H_INCLUDED
• I know hashlife is supposed to be the be all end all for game of life, but I would like some feedback on my code or post. – dylan Mar 14 '16 at 18:56