【问题标题】:How to implement operator>>(std::istream&, std::array<char, N>&)?如何实现运算符>>(std::istream&, std::array<char, N>&)?
【发布时间】:2017-05-31 02:58:23
【问题描述】:

我想要一个通用的、完全兼容的 istream “提取运算符”,如下所示:

template <typename CharT, size_t N, class Traits>
std::basic_istream<CharT, Traits>& operator>>(
    std::basic_istream<CharT, Traits>& in,
    std::array<CharT, N>& out)
{
    std::basic_string<CharT, Traits> buf; // this is not great
    in >> buf;
    if (buf.size() >= N) {
        in.setstate(std::ios::failbit); // is this the right thing to do?
        out[0] = 0;
    } else {
        std::copy(buf.begin(), buf.end(), out.data());
        out[buf.size()] = 0;
    }

    return in;
}

但这会不必要地分配和复制内存。我想避免这种情况。但我也想保留完整的功能,包括适当的空格和对std::noskipws 的支持等。如果使用 Boost 更容易的话,就可以了。

【问题讨论】:

  • 我相信您一定已经发现了一些问题,即一次只读取单个字符然后保持计数以检查溢出的方法。
  • @JohnZwinck 我认为您应该使用一些极端情况示例指定您想要的确切行为,例如" a b c", " abc ".
  • @Holt:我想要与使用std::string 而不是std::array&lt;char, N&gt; 完全相同的行为,如果字符串不适合包含空终止符的N,则添加一个设置失败。其余的行为和所有边缘情况都应该像普通的 C++ 字符串一样处理。

标签: c++ operator-overloading iostream istream


【解决方案1】:

不是一个完整的答案,但对于评论来说太大了:

重载仅操作数在namespace std 中的运算符会导致麻烦。这是因为两阶段查找,任何其他自定义 operator&gt;&gt; 都会隐藏您的 see example

通常通过在与操作数之一相同的命名空间中定义重载运算符来避免此问题,以便依赖于参数的查找始终找到该运算符。 (即使更近的命名空间有该名称,ADL 仍会搜索封闭的命名空间)。

但是,您无法使用该解决方案,因为它是 undefined behaviour to add your own functions to namespace std

我不确定解决此问题的首选解决方法是什么。如果您希望通过在模板参数上使用&gt;&gt; 的通用模板代码找到此函数,则需要显式地使您的重载可见,并在调用运算符的同一函数中使用using ::operator&gt;&gt;;。或者更简洁,using mystuff::operator&gt;&gt; 你将你的东西放在它自己的命名空间中。

【讨论】:

  • 哎哟!好吧,在我的特殊情况下,我愿意并且能够在我自己的命名空间中为std::array 使用 typedef 或包装类。所以我想我可以解决这个问题,但这很麻烦,所以感谢您指出!
【解决方案2】:

以下应该有效:

template <typename CharT, size_t N, class Traits>
std::basic_istream<CharT, Traits>& operator>>(
    std::basic_istream<CharT, Traits>& in,
    std::array<CharT, N>& out)
{
    in >> std::setw(N) >> out.data();
    if (!in.eof() && !std::isspace((CharT)in.peek(), in.getloc())) {
        out[0] = 0;
        in.setstate(in.rdstate() | std::ios::failbit);
    }
    return in;
}

据我所知,它的行为类似于您的带有 std::basic_string 的版本,只是它读取的字符数从不超过 N - 1

【讨论】:

    【解决方案3】:

    类似这样的:

    #include <array>
    #include <istream>
    #include <sstream>
    
    template <typename CharT, size_t N, class Traits>
    std::basic_istream<CharT, Traits>& operator>>(
        std::basic_istream<CharT, Traits>& in,
        std::array<CharT, N>& out)
    {
        auto first = out.begin();
        auto last = out.end();
        while (first != last and in)
        {
            in >> *first++;
        }
        return in;
    }
    
    int main()
    {
        std::istringstream ss { "a cd" };
        ss >> std::noskipws;
    
        std::array<char, 4> a;
        ss >> a;
    
        for (char c : a) {
            std::cout << "[" << c << "]\n";
        }
    }
    

    结果:

    [a]
    [ ]
    [c]
    [d]
    

    【讨论】:

    • 谢谢。如果输入流为空或失败,您能否使其执行空终止,包括将零作为第一个字符?
    • 这里有一个严重的错误:遇到空格时它不会停止(没有 noskipws)。常规的 std::string 运算符可以,这也应该。
    • @JohnZwinck 当您说“错误”时,这意味着您在遇到这些情况时已经考虑了一些已定义的行为。在编写此代码时,我没有意识到这些要求(您看到用户和实现者之间长期存在的问题吗?)。如果您正式陈述您的要求,我可以构建一个测试套件来检查它们,然后相应地迭代此代码,直到我们通过。这一切都始于了解并准确说明您想要什么行为。
    猜你喜欢
    • 2012-06-09
    • 2022-09-28
    • 2018-12-27
    • 1970-01-01
    • 2019-12-06
    • 1970-01-01
    • 2021-10-25
    • 2013-01-19
    • 1970-01-01
    相关资源
    最近更新 更多