Является ли boost::lexical_cast
избыточным теперь, когда С++ 11 представляет stoi
, stof
и семью, или есть ли какие-либо причины для его использования? (кроме того, что у вас нет компилятора С++ 11) Обеспечивают ли они точно такую же функциональность?
Является ли boost:: lexical_cast избыточным с С++ 11 stoi, stof и family?
Ответ 1
boost::lexical_cast
- обрабатывает больше видов преобразования, включая пары итераторов, массивы, строки C и т.д.
- предлагает один и тот же общий интерфейс (
sto*
имеют разные имена для разных типов) - чувствителен к локали (
sto*
/to_string
только частично, напримерlexical_cast
может обрабатывать тысячи разделителей, аstoul
обычно не работает)
Ответ 2
boost::lexical_cast
дает вам единый интерфейс между типами, который часто очень важен для общего кода.
В общем, согласованный интерфейс между типами для такой же функциональности позволяет использовать общий код. Например, следующий может использоваться как общий синтаксический анализатор из строковых токенов в std:: tuple:
template<typename T>
void fill(T& item, const std::string& token){
item = boost::lexical_cast<T>(token)
}
template<int N, typename ...Ts>
void parse(std::integral_constant<int, N>, std::tuple<Ts...>& info, std::vector<std::string>& tokens) {
fill(std::get<N>(info), tokens[N]);
parse(std::integral_constant<int, N - 1>, info, tokens);
}
template<typename ...Ts>
void parse(std::integral_constant<int, 0>, std::tuple<Ts...>& info, std::vector<std::string>& tokens) {
fill(std::get<0>(info), tokens[0]);
}
Вместо кортежа я часто использую boost fusion struct для десериализации некоторых токеновских строк непосредственно в структуру общим способом.
Ответ 3
boost:: lexical_cast больше, чем преобразование в отдельный набор типов:
struct A {};
std::ostream& operator << (std::ostream& stream, const A&) {
return stream;
}
struct B {};
std::istream& operator >> (std::istream& stream, B&) {
return stream;
}
int main(){
A a;
B b = boost::lexical_cast<B>(a);
}
Его сила и слабость - это принятие любой пары типов для преобразования через промежуточный std:: stringstream (где оптимизированный алгоритм применяется или не применяется).
Ответ 4
Эффективность, вы могли бы сделать сравнение, используя следующий код (это вариант моего сообщения здесь)
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <chrono>
#include <random>
#include <exception>
#include <type_traits>
#include <boost/lexical_cast.hpp>
using namespace std;
// 1. A way to easily measure elapsed time -------------------
template<typename TimeT = std::chrono::milliseconds>
struct measure
{
template<typename F>
static typename TimeT::rep execution(F const &func)
{
auto start = std::chrono::system_clock::now();
func();
auto duration = std::chrono::duration_cast< TimeT>(
std::chrono::system_clock::now() - start);
return duration.count();
}
};
// -----------------------------------------------------------
// 2. Define the convertion functions ========================
// A. Using boost::lexical_cast ------------------------------
template<typename Ret>
Ret NumberFromString(string const &value) {
return boost::lexical_cast<Ret>(value);
}
// B. Using c++11 stoi() -------------------------------------
int IntFromString(string const &value) {
return std::stoi(value);
}
// C. Using c++11 stof() -------------------------------------
float FloatFromString(string const &value) {
return std::stof(value);
}
// ===========================================================
// 3. A wrapper to measure the different executions ----------
template<typename T, typename F> long long
MeasureExec(std::vector<string> const &v1, F const &func)
{
return measure<>::execution([&]() {
for (auto const &i : v1) {
if (func(i) != NumberFromString<T>(i)) {
throw std::runtime_error("FAIL");
}
}
});
}
// -----------------------------------------------------------
// 4. Machinery to generate random numbers into a vector -----
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
FillVec(vector<T> &v)
{
mt19937 e2(1);
uniform_int_distribution<> dist(3, 1440);
generate(v.begin(), v.end(), [&]() { return dist(e2); });
}
template<typename T>
typename std::enable_if<!std::is_integral<T>::value>::type
FillVec(vector<T> &v)
{
mt19937 e2(1);
uniform_real_distribution<> dist(-1440., 1440.);
generate(v.begin(), v.end(), [&]() { return dist(e2); });
}
template<typename T>
void FillVec(vector<T> const &vec, vector<string> *result)
{
result->resize(vec.size());
for (size_t i = 0; i < vec.size(); i++)
result->at(i) = boost::lexical_cast<string>(vec[i]);
}
// -----------------------------------------------------------
int main()
{
std::vector<int> vi(991908);
FillVec(vi);
std::vector<float> vf(991908);
FillVec(vf);
std::vector<string> vsi, vsf;
FillVec(vi, &vsi);
FillVec(vf, &vsf);
cout << "C++ 11 stof function .. " <<
MeasureExec<float>(vsf, FloatFromString) << endl;
cout << "Lexical cast method ... " <<
MeasureExec<float>(vsf, NumberFromString<float>) << endl;
cout << endl << endl;
cout << "C++ 11 stoi function .. " <<
MeasureExec<int>(vsi, IntFromString) << endl;
cout << "Lexical cast method ... " <<
MeasureExec<int>(vsi, NumberFromString<int>) << endl;
return 0;
}
Когда выполняется с помощью
g++ -std = С++ 11 -Ofast -march = native -Wall -pedantic main.cpp && & &./a.out
Результаты
Функция С++ 11 stof. 540
Лексический метод литья... 559
С++ 11 stoi function.. 117
Лексический метод литья... 156
Специализированные функции С++ 11, судя по всему, лучше воспринимают. Но они точно такие, специализированные, и, таким образом, конструкция абстрактных интерфейсов сложнее, чем lexical_cast