【问题标题】:A template function for reading std::vector<T> from a stream用于从流中读取 std::vector<T> 的模板函数
【发布时间】:2018-08-21 23:13:39
【问题描述】:

我有一个从流中读取 std::vector 的模板 ReadVector 函数:

    template <class Stream, typename T>
    inline void ReadVector(Stream & s, std::vector<T> & items)
    {
        s.Read(reinterpret_cast<uint8_t *>(items.data()), items.size() * sizeof(T));
    }

具有布尔向量的特化:

    template <class Stream>
    void ReadVector(Stream & s, std::vector<bool> & x)
    {
    ...
    }

上面的代码可以编译,但我想让第一个函数只在 T 是算术类型时调用,所以条件应该是这样的:

std::enable_if<std::is_arithmetic<T>::value && !std::is_same(T, bool)::value

但我不知道语法是什么。

如果 T 不是 bool 且不是算术,我还希望 ReadVector 的另一个专业化。

我的第一个想法是这样的:

    template <class Stream, typename T>
    void ReadVector(Stream & s, std::vector<T> & items);

    template <class Stream, typename T>
    typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, void>::type ReadVector(Stream & s, std::vector<T> & items)
    {
    ...
    }

但这会导致对重载函数的模糊调用。

【问题讨论】:

  • 照原样,您得到了同一个函数模板的两个冲突声明:去掉第一个(如果您真的想保留它,则将其限制为相反的条件)。
  • @DietmarKühl,是的,你是对的!
  • 该代码存在许多潜在问题。在许多情况下,行为是未定义的或不可移植的,并且表示也可能因编译器选项而异。 最好使用 JSON 之类的文本格式,因为它更易于维护,并且更易于调试和手动修复损坏的数据。如果数据大小很重要,您可以像 DOCX 等现代 Office 文件格式一样压缩数据。
  • 当你传递vector&lt;int&gt;时,模板参数替换后,两个函数都在重载解析集中,因为它们“几乎”相同,这会导致歧义。
  • @Phil1970 我在设备上本地读写文件,所以我可以使用vector::data()。

标签: c++


【解决方案1】:

想通了!

    template <class Stream, typename T>
    typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, void>::type ReadVector(Stream & s, std::vector<T> & v)
    {
        s.Read(reinterpret_cast<uint8_t *>(v.data()), v.size() * sizeof(T));
    }

    template <class Stream, typename T>
    typename std::enable_if<std::is_class<T>::value, void>::type ReadVector(Stream & s, std::vector<T> & v)
    {
        for (auto & elem : v)
        {
            Read(s, elem);
        }
    }

    template <class Stream>
    void ReadVector(Stream & s, std::vector<bool> & x)
    {
    ...
    }

【讨论】:

  • 虽然它可能适用于某些类型,但它非常脆弱。如果你想移植到另一个平台,或者只是改变编译器选项,它就不能再正常工作了。它对您的班级所做的更改也非常敏感。像重新排序成员这样简单的事情会破坏代码。
  • @Phil1970 你能指出为什么这是脆弱的,编译器选项如何破坏它?
  • 例如,如果 T 是一个类并且您修改了编译器选项,如果您不小心,T 的大小可能会发生变化。而且在应用程序的生命周期中,您可能会在 T.... 中添加一些字段,因此二进制流非常脆弱。
【解决方案2】:

我认为你的方法是错误的!

为什么不直接使用向量构造函数?

std::vector<int>  v(std::istream_iterator<int>(stream), std::istream_iterator<int>());

当可以在向量初始化器中完成时不需要函数。

如果你想做一些花哨的事情(比如从流中读取原始数据),你只需定义一个类来读取特定类型的数据。

struct RawDataToInt
{
     int  value;
     operator int() {return value;}
     friend std::istream& operator>>(std::istream& str, RawDataToInt& v)
     {
         return str.read(reinterpret_cast<char*>(&v.value), sizeof(int));
     }
};
....
std::vector<int>  v(std::istream_iterator<RawDataToInt>(stream), std::istream_iterator<RawDataToInt>());

【讨论】:

  • @AlexeyStarinsky 没有改变任何东西。只需定义从 RAW 到内部类型的适当转换。
  • 这是一个不错的选择。可能我对 std::stream 的理解不够,但我有点担心 std::stream 总是被缓冲的事实,在某些情况下,我需要一个直接在 Win32 平台上调用 ::ReadFile 的流的实现和我需要用输出流调用 ::FlushFileBuffers。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-21
  • 2016-11-07
  • 2012-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多