Simple generic cartesian product in C++
Don't limit Cartesian products to arithmetic types
You shouldn't limit your class to only allow products of vectors of a type that satisfies std::is_arithmetic. Why shouldn't I be able to do a Cartesian product of a std::string and a std::chrono::time_point for example? There is no actual multiplication involved in a Cartesian product after all.
Your class will likely force copies of vectors to be made
In this constructor:
cartesian_product(std::vector&lt;std::vector&lt;T&gt;&gt; const&amp; sequences) {...}

The parameter sequences is a reference, so you might think that this doesn't cause any copies to be made. However, that is only true if the caller already has a vector of vectors lying around. In your own example usage in main(), you have four vectors a, b, c and d, but then you create the vector of vectors s which causes a copy to be made of the four original vectors. And the following line constructs a temporary that also makes copies:
cartesian_product&lt;int&gt; r(std::vector&lt;std::vector&lt;int&gt;&gt;{ a, b, c, d });

Support more containers besides std::vector
You only support products of std::vector&lt;T&gt;, but what if I have my data in a regular array, a std::array, a std::list or any other container? Again, it is an unnecessary restriction. Try to rewrite it so any type of container works. Either have it accept a reference to a container object directly, or have it accept iterators like the STL algorithms do.
Return a tuple instead of a vector
A std::vector will allocate memory on the heap, which will impact performance. And since you know the size of the result, you can return a std::tuple instead, which has the advantage that it can be unpacked using C++17's structured bindings. Apart from that, with a std::tuple the value types of each input vector/array/etc can be different, whereas your solution requires all of them to be the same,
Add iterators to your class
Indeed, it makes sense to add iterators to your class. In fact, it already basically keeps track of everything that an iterator would do, and next() is the equivalent of an iterator's operator++(). Ideally, you want to be able to write something like:
std::vector&lt;std::string&gt; a_vector{&quot;foo&quot;, &quot;bar&quot;, &quot;baz&quot;};
std::list&lt;int&gt; b_vector{4, 5, 6, 7};

for (auto [a_element, b_element]: cartesian_product(a_vector, b_vector))
    std::cout &lt;&lt; a_element &lt;&lt; &quot;, &quot; &lt;&lt; b_element &lt;&lt; &quot;\n&quot;;

And then expect the following result:
foo, 4
foo, 5
foo, 6
foo, 7
bar, 4
bar, 5
...

This also avoids the need for a has_next() function.