A from_string() function (inverse of std::to_string)
Just going to review your original code and not the edit.

inline std::istringstream&amp; get_istringstream(){
    static thread_local std::istringstream stream;
    stream.str("");
    return stream;
}

template &lt;typename T&gt;
inline T from_string(const std::string&amp; s){
    auto&amp; iss(get_istringstream());
    iss.str(s);
    T result;
    iss &gt;&gt; result;
    return result;
}


As Jerry Coffin mentioned, you are basically reinventing boost::lexical_cast.  I wasn't sure if I wanted to review this or flag it as broken code, but it technically compiles and it technically works for one conversion (perhaps a discussion for meta-cr).  Test your code thoroughly.




  Are there other considerations I've overlooked, performance-wise (*) or usability-wise?


You don't handle errors.  Consider someone doing this:

auto x1 = from_string&lt;int&gt;("Not an int");


What is the value of x1?  

    T result;      // uninitialized variable.
    iss &gt;&gt; result; // operator&gt;&gt; fails silently, sets failbit
    return result; // return uninitialized variable.


The result is undefined behavior since we are returning an uninitialized value that was never assigned to.  Remember to check that the reading of a value was successful.  Notify the callee whenever that an error, like invalid argument or out of range, has occurred.

There are different error handling strategies today.  The common ones are exceptions, std::system_error, and Alexandrescu's expected&lt;T, E&gt; (currently being proposed for standardization).



Let's say someone does something common, like use your function more than once.

auto x1 = from_string&lt;int&gt;("42");
auto x2 = from_string&lt;int&gt;("43");


The first line returns 42, that is fine.  The second line has an issue.

    T result;      // uninitialized variable.
    iss &gt;&gt; result; // eofbit enabled from previous stream consumption
    return result; // return uninitialized variable.


Undefined behavior again.  The original code had

std::istringstream iss;


On construction, isss internals were initialized (data and error state).  In your refactored code, you wanted to mimic a newly constructed object

inline std::istringstream&amp; get_istringstream(){
    static thread_local std::istringstream stream;
    stream.str("");
    return stream;
}

    auto&amp; iss(get_istringstream());


Turns out your refactored code wasn't equivalent.  You forgot to clear the error state.



Consider the following code:

auto x1 = from_string&lt;double&gt;("42,004");


What is the value of x1?  Conversion of arithmetic types using separators is dependent on the locale of the stream.  The possible results are


forty-two thousand and four,
forty two and four one-thousandths, or
forty-two.  


If you are fine with this behaviour, document it.  If you want to ensure you always use the behavior of a specific locale by default, consider imbuing before reading.




  Perhaps I should mention my motivation here is partly how I found it strange that there's no std::from_string().


&lt;std::string&gt; provides functions to convert to arithmetic types


Reals - std::stod, std::stold, std::stof
Signed integrals - std::stoi, std::stol, std::stoll, and 
Unsigned integrals std::stoul, std::stoull.  


You could also consider copying the naming scheme from these and have string_to&lt;T&gt;.

For general purpose conversions, the ones that immediately come to mind are  boost::lexical_cast (stream-based) and boost::spirit (policy-based).  Boost also has boost::convert, an adapter library built to work with various conversion utilities (strtol, boost::spirit, boost::lexical_cast, and C++-Streams).