Object oriented, runtime expandable Sieve of Eratosthenes
Initialize your class members in the declaration

class Sieve {
public:
    unsigned expand(unsigned upperlimit);
    bool isPrime(unsigned n) const;
private:
    unsigned limit = 0;
    std::vector&lt;bool&gt; composite{};
};


is easier to read because you don't need to jump to the constructor to see the initial values and now you don't even need a constructor anymore.
In case you do write constructors and use the initialization list syntax it will override the declaration initialization for those members, so your constructors can concentrate on initializing members that are different than the default from the declaration, again increasing readability.

Limit your nesting depth

Sieve::expand is moving quite far to the right. A way to avoid that is to use early exits instead of nesting:

unsigned Sieve::expand(unsigned upperlimit) {
    if (upperlimit &lt;= limit)
        return limit;
    composite.resize(upperlimit);
    composite[0] = true; // 1 is not prime
    const unsigned sqrtLimit = std::ceil(std::sqrt(upperlimit));
    // std::cout &lt;&lt; "old limit = " &lt;&lt; limit &lt;&lt; ", new limit = " &lt;&lt; upperlimit &lt;&lt; "\n";
    for (unsigned i = 3; i &lt;= sqrtLimit; i += 2) {
        if (composite[i / 2])
            continue;
        const unsigned start = i * std::max(i, (((limit + i) / i) | 1));
        // std::cout &lt;&lt; "Starting with i = " &lt;&lt; i &lt;&lt; ", from " &lt;&lt; start &lt;&lt; "\n";
        for (unsigned j = start; j &lt;= upperlimit; j += i + i) {
            composite[j / 2] = true;
            // std::cout &lt;&lt; j &lt;&lt; " is composite because it's a multiple of " &lt;&lt; i &lt;&lt; "\n";
        }
    }
    limit = upperlimit;
    return limit;
}


Declare variables as late as possible

Inside main you don't need m and n until you are inside the for loop.

Do at least basic input error checking

You wrote that the program expects valid input, but that is not really acceptable. When a user enters something that cannot be parsed as an int you read uninitialized memory which is undefined behavior and bad things happen.
if (!(std::cin &gt;&gt; m &gt;&gt; n)) return -1; at least gives your program defined behavior without much effort. You could also throw an exception or print a proper message and let the user try again, but having std::cin outside an if is usually a problem.

Better name-giving-ness

limit and upperlimit seem less nice than size and requested_size.
T is usually a template parameter and it probably shouldn't be the only variable name that is capitalized. m and n don't tell me much either. I would prefer T-> test_cases, n->upper, m->lower or something more descriptive.

Consistent parenthesis for ifs

Arguably it is a matter of style if you prefer

if (n &gt; limit) 
        throw std::range_error("n must be less than limit");


or

if (sieve.isPrime(i)) {
    std::cout &lt;&lt; i &lt;&lt; ' ';
}


but you should pick a style and stick to it.
Now, going through the code and adding brackets around one-line if-statements is a stupid waste of time, fortunately clang-tidy -fix *.cpp "-checks=-*,readability-braces-around-statements" -- -std=c++14 can automatically fix that for you.