do_decimal_point
а также do_thousands_sep
кажется, полностью игнорируется моим потоком.
То, что я хочу сделать, это использовать период для моего thousands_sep
и запятая для моего decimal_point
в get_money
. Поэтому я переопределяю moneypunct
но это просто игнорируется 🙁
struct punct_facet : public moneypunct<char> {
char_type do_decimal_point() const { return ','; }
char_type do_thousands_sep() const { return '.'; }
};
int main()
{
istringstream USCurrency("1,234.56 -1,234.56 1.234,56 -1.234,56");
USCurrency.imbue(locale(locale("en-US"), new punct_facet));
int index = 0;
long double value;
do{
value = 0.0;
USCurrency >> get_money(value, true);
cout << ++index << ": " << value << endl;
} while (value == 123456.0 || value == -123456.0);
return 0;
}
Я ожидаю, что это просто вывод:
1: 123
Но вместо этого я получаю:
1: 123456
2: -123456
3: 123
Что я делаю неправильно? Я использую Visual Studio 2013, в случае, если это может быть очевидно из "en-US"
,
РЕДАКТИРОВАТЬ:
Я обнаружил, когда я ставлю точку останова в do_decimal_point
или же do_thousands_sep
что это никогда не ударил. Я не уверен, почему нет, но эта информация, кажется, имеет отношение к проблеме.
Это решение действительно просто объяснение ответ дан здесь.
И конструктор копирования, и оператор присваивания удаляются moneypunct
реализация. Что оставляет два плохих варианта для построения punct_facet
:
moneypunct
члены в punct_facet
и позвони всем moneypunct
виртуальные функции в punct_facet
конструктор для их инициализации. Это имеет очевидный недостаток punct_facet
объект в два раза жирнее, чем должен быть, и его конструктор работает дольше, чем это строго необходимо.moneypunct
к punct_facet
, Это имеет очевидный недостаток — не быть кроссплатформенным и намеренно игнорировать дизайн стандартной реализации.Для этого ответа я выбрал плохой вариант 2, потому что реализация moneypunct
уже зависит от компилятора для любого аргумента конструкции, кроме: ""
, "C"
, или же "POSIX"
и потому что есть открытая ошибка против удаленного moneypunct
конструктор копирования и оператор присваивания. (Кстати если moneypunct
аргумент конструкции корректируется вариант 2 работает в gcc 5.1.0 как хорошо, но это будет не работать в Clang 3.6.0.) Надеемся, что Microsoft вскоре предоставит более функциональный обходной путь для этой ошибки, и нам не придется использовать ни одну из плохих опций.
Так что если punct_facet
реализован так:
template <typename T>
class punct_facet : public T {
private:
void Init(const T* money){
const auto vTablePtrSize = sizeof(void*);
memcpy(reinterpret_cast<char*>(this) + vTablePtrSize, reinterpret_cast<const char*>(money) + vTablePtrSize, sizeof(T) - vTablePtrSize);
}
protected:
typename T::char_type do_decimal_point() const {
return typename T::char_type(',');
}
typename T::char_type do_thousands_sep() const {
return typename T::char_type('.');
}
public:
punct_facet(){
Init(&use_facet<T>(cout.getloc()));
}
punct_facet(const T* money){
Init(money);
}
};
Вы можете построить с любым из punct_facet
конструкторы, и вы получите ожидаемый результат:
123
Чтобы использовать конструктор по умолчанию, вам нужно добавить cout.imdue(locale("en-US"));
на вершине main
и измени свой imdue
заявление для:
USCurrency.imbue(locale(locale("en-US"), new punct_facet<moneypunct<char, true>>()));
Чтобы использовать пользовательский конструктор, вам нужно всего лишь изменить свой imdue
заявление для:
USCurrency.imbue(locale(locale("en-US"), new punct_facet<moneypunct<char, true>>(&use_facet<moneypunct<char, true>>(locale("en-US")))));
Конструктор по умолчанию предпочтительнее, так как расхождение между типом шаблона и аргументом конструктора может привести к некоторому плохому поведению.
Одна небольшая нота, ваш USCurrency
не использует международный формат валюты, поэтому нет необходимости использовать moneypunct<char, true>
, moneypunct<char>
будет работать просто отлично. Просто не забудьте изменить его везде как несоответствие между аргументами шаблона punct_facet
и аргументы, используемые в get_money
снова приведет к неожиданному поведению, которое вы видели.