C++ conversion from std::string to arithmetic type using std::locale::classic() I recently had a bug where extracting a decimal from a string failed due to locale settings. That is, some locales use a , as a decimal point, rather than a .. An important goal is that the conversion function is deterministic.

I have usually used boost::lexical_cast for such tasks, but my understanding is that this is reliant on the global application locale. I have therefore implemented a variant of lexical_cast that uses the std::locale::classic "C" locale for the conversion.

#include &lt;type_traits&gt;
#include &lt;locale&gt;
#include &lt;string&gt;
#include &lt;sstream&gt;
#include &lt;stdexcept&gt;

namespace typeconv
{

/**
 * Convert @c str to a T
 *
 * @param str string to convert to a T
 * @return the value contained within @c as a T
 *
 * @pre @c str is arithmetic and can be converted to a T
 * @note std::locale::classic() is used for the conversion.
 *
 * @throws std::invalid_argument if str cannot be converted to an object of type T.
 */
template&lt;typename T&gt;
inline auto lexical_cast(const std::string&amp; str)
    -&gt; typename std::enable_if&lt;std::is_arithmetic&lt;T&gt;::value, T&gt;::type
{
    std::istringstream istr(str);
    istr.imbue(std::locale::classic());

    T val;
    istr &gt;&gt; val;

    if (istr.fail())
        throw std::invalid_argument(str);

    return val;
}

}


I plan to extend the template function in future to include other conversions, such as an arithmetic type to std::string. Any comments on the implementation are welcome.
I see two things I'd change here.

The first is that right now it supports only conversion from string to the specified (arithmetic) type. Sometimes you want (for example) to convert to a string, not just from one. To support that, we can add a second template parameter for the type of the input argument.

My second big point is that you're starting from a problem where the locale to use was hard-wired into boost::lexical_cast. But instead of fixing (what seems to me like) the real problem, you've just hard-wired a different locale.

Instead of that, I'd prefer to specify a locale to use by default, but also allow the user to specify a locale to use for the conversion, if they choose to do so.

Putting those together, we get a function something like this:

template&lt;typename T, typename U&gt;
inline auto lexical_cast(U const&amp; in, std::string const &amp;name = "C")
{
    std::stringstream istr;
    istr.imbue(std::locale(name));
    istr &lt;&lt; in;

    std::string str = istr.str(); // save string in case of exception

    T val;
    istr &gt;&gt; val;

    if (istr.fail())
        throw std::invalid_argument(str);

    return val;
}


Now we can do things like:

// Treat as German input
auto x = lexical_cast&lt;double&gt;("1.234,567", "de");


or:

// convert to string in user's locale
auto s = lexical_cast&lt;std::string&gt;(123456.78, "");


Finally, I'd note one basic difference between this and boost::lexical_cast. This throws an exception if and only if it is unable to convert the input to a result of the output type (at all). Boost lexical_cast not only requires that you be able to convert the input to the output type, but also that when the conversion completes that it consume the entire input.

Consider, for example, something like:

int x = lexical_cast&lt;int&gt;("1 x");


With your lexical_cast, this will succeed because it successfully converted the 1 to an int. With Boost lexical_cast, this will throw an exception because even though it converted 1 to an int, that didn't use the x.