Pre-calculate the winning combinations for an n-sized tic tac toe board using C++ functional programming
Here are some suggestions.  This answers uses the Range-v3 library and assumes

#include &lt;range/v3/all.hpp&gt;

namespace view = ranges::view;



Please include the #includes and supply a small test program in the future.  You probably have written them anyway, so why not post them to save reviewers' time? :)
int may be too small for indexes.  Consider using std::size_t.  You can define a type alias for flexibility.

using index_t = std::size_t;

The type std::vector&lt;std::vector&lt;index_t&gt;&gt; occurs many times.  Save time by writing

using rule_t = std::vector&lt;index_t&gt;
using rules_t = std::vector&lt;rule_t&gt;;

The concept "\$n \times n\$ board" only makes sense when \$n\$ is a positive integer.  Therefore, enforce the pre-condition \$n \ge 1\$.  Also, the name x is a bit vague.  size may be better.

rules_t rules(index_t size)
{
    if (size == 0)
        throw std::invalid_argument{"..."};
    // ...
}

rules(1) currently returns {{0}, {0}, {0}, {0}}, which is definitely wrong.  It should return {{0}}.  Your general logic is "\$n\$ rows + \$n\$ columns + \$2\$ diagonals", which only applies to \$n \ge 2\$.  The easiest approach would be a special case:

rules_t rules(index_t size)
{
    // ...
    else if (size == 1)
        return {{0}};
}

Following the previous bullet, why not make separate functions to make your logic clear?

rules_t rules(index_t size)
{
    // ...
    else {
        auto rows = rules_row(size);
        auto columns = rules_column(size);
        auto diagonals = rules_diagonal(size);
        return ranges::to&lt;rules_t&gt;(view::concat(rows, columns, diagonals));
    }
}


where view::concat concatenates the three vectors and ranges::to converts the result to the desired return type.  (Note that view::concat is disabled with rvalues to prevent dangling iterators, so we first store the subvectors.)
The rules_row function is easy to write:

rules_t rules_row(index_t size)
{
    return view::ints(index_t(0), size * size) | view::chunk(size);
}


view::ints generate a left-inclusive sequence of integers {0, 1, 2, ..., size * size - 1}, and view::chunk(size) breaks it into chunks each of size size.
The rules_column function is a bit tricky, because the Range library does not provide a function like chunk that splits like this.  We do have a function stride, so we can write a manual "loop":  (it took me quite a while to figure this out, so tell me if there is a better way!)

rules_t rules_column(index_t size)
{
    return view::ints(index_t(0), size) |
               view::transform([=](index_t col) {
                   return view::ints(index_t(col), size * size) | view::stride(size);
               });
}


view::ints(index_t(0), size) generates the column numbers.  Each of them is passed to the lambda.  The lambda returns the corresponding rule for each column.
The rules_diagonal function is moderately simple:

rules_t rules_diagonal(index_t size)
{
    return {
        view::ints(index_t(0), size) |
            view::transform([=](index_t r) { return r * (size + 1); }),
        view::ints(index_t(1), size + 1) |
            view::transform([=](index_t r) { return r * (size - 1); })
    };
}


Here, we always have two rules: one for the primary diagonal

{0 * size + 0, 1 * size + 1, 2 * size + 2, ..., (size - 1) * size + (size - 1)}


which is equivalent to

{0, 1, 2, ..., size - 1} * (size + 1)


and the other for the secondary diagonal

{size - 1, 2 * size - 2, 3 * size - 3, ... size * size - (size - 1)}


which is equivalent to

{1, 2, 3, ..., size + 1} * (size - 1)

Putting everything together:

#include &lt;vector&gt;
#include &lt;range/v3/all.hpp&gt;

namespace view = ranges::view;

using index_t = std::size_t;
using rule_t = std::vector&lt;index_t&gt;;
using rules_t = std::vector&lt;rule_t&gt;;

rules_t rules_row(index_t size)
{
    return view::ints(index_t(0), size * size) | view::chunk(size);
}

rules_t rules_column(index_t size)
{
    return view::ints(index_t(0), size) |
               view::transform([=](index_t col) {
                   return view::ints(index_t(col), size * size) | view::stride(size);
               });
}

rules_t rules_diagonal(index_t size)
{
    return {
        view::ints(index_t(0), size) |
            view::transform([=](index_t r) { return r * (size + 1); }),
        view::ints(index_t(1), size + 1) |
            view::transform([=](index_t r) { return r * (size - 1); })
    };
}

rules_t rules(index_t size)
{
    if (size == 0)
        throw std::invalid_argument{"A board cannot have size zero"};
    else if (size == 1)
        return {{0}};
    else {
        auto rows = rules_row(size);
        auto columns = rules_column(size);
        auto diagonals = rules_diagonal(size);
        return view::concat(rows, columns, diagonals);
    }
}


(live demo, tests the function for size = 1, 2, 3, ..., 10)