语言环境构面构造函数被忽略

locale Facet Constructor Ignored

locale Facet constructor:

Constructs a copy of other except for the facet of type Facet (typically deduced from the type of the argument) which is installed from the argument facet. If facet is NULL, the constructed locale is a full copy of other. The locale constructed in this manner has no name.

我尝试使用我的 Facet 进行构造,但是当我在我的 do_decimal_pointdo_thousands_sep 中放置一个断点时,它们永远不会被调用 :(

我可以看到 Facet 被传入,但它被传递到标准库实现文件中,所以我看不到是否对它做过任何事情。

我已经在 Visual Studio 2013、Clang 3.6.0 和 gcc 4.9.2 上试过了。 它们中的所有 的行为就好像我从未通过 Facet 只是使用另一个 locale 的行为。

我在任何编译器中都找不到针对此构造函数的任何错误。我认为我这样做是对的。为什么我不能让 locale 使用我的 Facet 进行构建?

编辑:

I have added an example. It's interesting to note that the Facet does seem to be picked up but not used with get_money. I'm linking a live example of this(必须使用 locale("C") 而不是 locale("en-US")):

class Foo : public std::moneypunct<char> {
protected:
    char_type do_decimal_point() const {
        cout << "Hit Foo::do_decimal_point";
        return ',';
    }
    char_type do_thousands_sep() const {
        cout << "Hit Foo::do_thousands_sep";
        return '.';
    }
};

int main()
{
    cout.imbue(locale(locale("en-US"), new Foo));

    const moneypunct<char>* temp = &use_facet<std::moneypunct<char>>(cout.getloc());

    cout << temp->decimal_point() << endl << temp->thousands_sep() << endl;

    istringstream USCurrency("1,234.56 -1,234.56 1.234,56 -1.234,56");
    USCurrency.imbue(cout.getloc());

    long double value;

    USCurrency >> get_money(value, true);

    return 0;
}

这输出:

Hit Foo::do_thousands_sepHit Foo::do_decimal_point,
.

我希望它输出:

Hit Foo::do_thousands_sepHit Foo::do_decimal_point,
.
Hit Foo::do_thousands_sepHit Foo::do_decimal_point

编辑2:

看来 moneypunct<char> 无法继承,因为它没有正确构造,除非它是由 locale 内部构造的。这至少在 Visual Studio 上是一个问题,因为它决定了是否由 grouping 使用 thousands_sep。解决方法可能是完全重新实现 moneypunct<char> 的功能。我现在正在修补它。与此同时,我还在这里添加了一个错误:https://connect.microsoft.com/VisualStudio/feedback/details/1524749/inheriting-from-moneypunct-requires-use-of-unavailable-construction-information

事实是,do_decimal_placedo_thousands_place受到get_money的尊重。困难在于被继承的 moneypunct 是默认构造的,所以指导 get_money 调用 do_decimal_placedo_thousands_place 的支持信息不是设置。

Visual Studio 对 moneypunct 的实现提供了两个 public 构造函数:

  1. moneypunct()
  2. moneypunct(const _Locinfo& _Lobj, size_t _Refs = 0, bool _Isdef = false)

locale 的构造函数调用第二个 moneypunct 构造函数。创建适当的 _Locinfo 是问题的关键,因为该信息似乎是特定于实现的。 linked Visual Studio Bug 请求一种构建功能性 moneypunct 的方法,而无需访问实现细节。代替此信息,所有 moneypunct 字段都必须经过处理。

因为这个问题是关于扩展预期的工作 moneypunct 最简单的方法是使用赋值运算符或复制构造函数。 坏消息:这两个都被删除了。所以 punct_facet(const money_punct&) 需要在内部编写来实现复制构造函数的行为。需要复制的值对应所有需要被覆盖的虚函数punct_facet。最后,您的 class 看起来将类似于:

template <typename T>
class punct_facet : public T {
protected:
    typename T::string_type m_grouping;
    typename T::string_type m_curr_symbol;
    typename T::string_type m_positive_sign;
    typename T::string_type m_negative_sign;
    int m_frac_digits;
    typename T::pattern m_pos_format;
    typename T::pattern m_neg_format;

    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('.');
    }

    typename T::string_type do_grouping() const {
        return m_grouping;
    }

    typename T::string_type do_curr_symbol() const {
        return m_curr_symbol;
    }

    typename T::string_type do_positive_sign() const {
        return m_positive_sign;
    }

    typename T::string_type do_negative_sign() const {
        return m_negative_sign;
    }

    int do_frac_digits() const {
        return m_frac_digits;
    }

    typename T::pattern do_pos_format() const {
        return m_pos_format;
    }

    typename T::pattern do_neg_format() const {
        return m_neg_format;
    }
public:
    punct_facet(const T& defaultFacet) : m_grouping(defaultFacet.grouping()),
                                         m_curr_symbol(defaultFacet.curr_symbol()),
                                         m_positive_sign(defaultFacet.positive_sign()),
                                         m_negative_sign(defaultFacet.negative_sign()),
                                         m_frac_digits(defaultFacet.frac_digits()),
                                         m_pos_format(defaultFacet.pos_format()),
                                         m_neg_format(defaultFacet.neg_format()) {}
};

编辑:

这个解决方案是跨平台的,但也不能令人满意,因为必须添加到 punct_facet 中的所有成员已经存在 moneypunct 中。我不知道这种育肥的干净解决方法。此处提供了特定于编译器的 hack:

如果 Visual Studio 将 v-table 指针作为对象布局中的第一项,这将导致 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);
    }
};

顺便提一下,Clang 3.6.0 不支持 punct_facet 的实现,但 gcc 5.1.0 支持 http://coliru.stacked-crooked.com/a/e4a1d88b560d6d1b