Шаблон дружественной строки для нумерации c в C ++ - PullRequest
48 голосов
/ 19 февраля 2020

В стандартной библиотеке C ++ есть функции для преобразования из строки в число c типы:

stoi
stol
stoll
stoul
stoull
stof
stod
stold

, но я считаю утомительным использовать их в коде шаблона. Почему нет шаблонных функций, например:

template<typename T>
T sto(...)

для преобразования строк в числовые c типы?

Я не вижу никаких технических причин, чтобы их не иметь, но, возможно, я Я что-то упустил. Они могут быть специализированными для вызова базовых именованных функций и использования enable_if / concepts для отключения нечисловых c типов.

Существуют ли в стандартной библиотеке дружественные шаблону альтернативы для преобразования строки в числительное c типов и наоборот эффективно?

Ответы [ 5 ]

40 голосов
/ 19 февраля 2020

Почему нет шаблонных функций, например:

C ++ 17 имеет такую ​​обобщенную c функцию «строка в число», но названа по-другому. Они пошли с std::from_chars, который перегружен для всех числовых типов c.

Как видите, первая перегрузка принимает любой целочисленный тип в качестве выходного параметра и назначит значение к нему, если это возможно.

Его можно использовать так:

template<typename Numeric>
void stuff(std::string_view s) {
    auto value = Numeric{};

    auto [ptr, error] = std::from_chars(s.data(), s.data() + s.size(), value);

    if (error) {
        // error with the conversion
    } else {
        // conversion successful, do stuff with value
    }
}

Как видите, он может работать в общем c контексте.

13 голосов
/ 19 февраля 2020

Это не шаблон, и он не работает с локалями, но если это не ограничитель показа, то C ++ 17 уже имеет то, что вы хотите: std::from_chars

Существуют перегрузки для всех целочисленных типов и типов с плавающей запятой, и интерфейс одинаков за исключением последних параметров, которые отличаются для целочисленных типов и типов с плавающей запятой соответственно (но если по умолчанию все в порядке, то вам не нужно изменить что-либо). Поскольку эта функция не поддерживает локали, она также довольно быстрая. Он превзойдет любую другую функцию преобразования строки в значение, и обычно это на порядки.

Существует очень хорошее видео CPPCON о <charconv> (заголовок from_chars от Stephan T. Lavavej, о его использовании и производительности можно посмотреть здесь: https://www.youtube.com/watch?v=4P_kbF0EbZM

9 голосов
/ 19 февраля 2020

Вы не получите много, потому что в выражении типа

int x = sto("1");

Нет (простого) способа определить желаемый тип для параметра шаблона. Вам нужно было бы написать

int x = sto<int>("1");

, что в некоторой степени противоречит цели предоставления функции c. С другой стороны,

template<typename T>
void sto(std::string x,T& t);

будет полезным, как вы поняли. В C ++ 17 есть std::from_chars, который делает это более или менее точно (это не шаблон, а набор перегрузок, и он использует указатели на символы вместо строки, но это лишь незначительные детали ).

PS Нет простого способа вывести желаемый тип в вышеприведенном выражении, но есть способ. Я не думаю, что в основе вашего вопроса была именно та подпись, о которой вы просили, и я не думаю, что следующее является хорошим способом для ее реализации, но я знал, что есть способ сделать вышеупомянутую int x = sto("1"); компиляцию, и мне было любопытно чтобы увидеть это в действии.

#include <iostream>
#include <string>

struct converter {
    const std::string& x;
    template <typename T> operator T() { return 0;}
};

template <> converter::operator int() { return stoi(x); }
template <> converter::operator double() { return stod(x); }
converter sto(const std::string& x) { return {x}; }

int main() {
    std::string s{"1.23"};
    int x = sto(s);
    double y = sto(s);
    std::cout << x << " " << y;
}

Это работает как задумано, но имеет серьезные недостатки, может быть, самое главное, это позволяет писать auto x = sto(s);, ie, это легко использовать неправильно.

5 голосов
/ 19 февраля 2020

Решение, совместимое со всеми (даже более старыми компиляторами C ++, такими как C ++ - 98), заключается в использовании boost :: lexical_cast , который представляет собой шаблон для преобразования между числовыми c и строковыми типами в обоих направлениях.

Пример:

short myInt = boost::lexical_cast<short>(*argv);
std::string backToString = boost::lexical_cast<std::string>(myInt);

См .: https://www.boost.org/doc/libs/1_42_0/libs/conversion/lexical_cast.htm

3 голосов
/ 20 февраля 2020

В старых версиях C ++ stringstream - ваш друг. Если я правильно понимаю, то вам может быть интересно следующее. Это C ++ 11.

https://wandbox.org/permlink/nUNiUwWWTr7a0NXM

#include <sstream>
#include <string>
#include <iostream>

template<typename T, typename String>
T sto(const String & str) {
    T val;
    std::stringstream ss(str);
    ss >> val;
    return val;
}

template<typename T, typename String>
void sto(const String & str, T & val) {
    std::stringstream ss(str);
    ss >> val;
}

int main() {   
    std::cout << sto<float>("1.1") << ", " << sto<int>(std::string{"2"});

    // An alternative version that infers the type 
    double d;
    sto("3.3", d);
    std::cout << ", " << d;
}

Этот метод работает в C ++ 11 и является довольно общим. По моему опыту, этот метод является надежным, но не самым эффективным.

...