In other RDBMS (like SQL Server before 2008 - as per Paul's comment) one might cross join to a subquery with UNION ALL SELECT, but there are more convenient and efficient options in Postgres.
And you don't need a CTE for this. You can use it, but it has no performance benefit.
1. Provide a set with a VALUES expression
The manual:
VALUES computes a row value or set of row values specified by value
expressions. It is most commonly used to generate a "constant table"
within a larger command, but it can be used on its own.
SELECT *
FROM (VALUES (1), (2)) i(i)
CROSS JOIN tbl t;
2. Provide an array and unnest()
... with an array constructor:
SELECT *
FROM unnest(ARRAY[1,2]) i
CROSS JOIN tbl t;
... with an array literal:
SELECT *
FROM unnest('{1,2}'::int[]) i
CROSS JOIN tbl t;
Add ORDER BY i, t.col1 if you need the sort order in your result.
About row and array syntax: