【问题标题】:(De)serializing an enum class(反)序列化枚举类
【发布时间】:2019-05-31 23:42:53
【问题描述】:

我正在尝试序列化和反序列化(使用QDataStream,但这在这里无关紧要)enum class 变量:

enum class Type : char
{
    Trivial,
    Complex
};

序列化很简单:

QDataStream &operator<<(QDataStream &stream, Type type)
{
    return stream << static_cast<char>(type);
}

但是反序列化不是:

QDataStream &operator>>(QDataStream &stream, Type &type)
{    
    return stream >> static_cast<char &>(type);
}

显然,引用enum classstatic_cast 对其基础类型的引用是不允许的。此外,“明显”的解决方案:

QDataStream &operator>>(QDataStream &stream, Type &type)
{    
    return stream >> reinterpret_cast<char &>(type);
}

实际上可能是非法的并且没有由标准according to the answer to this question 定义,因为等效表达式return stream &gt;&gt; (*static_cast&lt;char *&gt;(static_cast&lt;void *&gt;(&amp;type))); 在那里被声明为非法(或者更确切地说,标准没有定义)。如果是这种情况,我需要这样做:

QDataStream &operator>>(QDataStream &stream, Type &type)
{    
    char c = 0;
    stream >> c;
    type = static_cast<Type>(c);
    return stream;
}

这不漂亮,是 4 行而不是 1 行等等。对于这样一个(看似)简单的事情,我觉得这很不必要。

我的问题:当将对enum class 变量的引用转换为其基础类型的引用时,reinterpret_cast 或通过void* 等效的static_cast 是否真的非法(标准未定义)?

【问题讨论】:

  • 您提出的问题与您链接的问题有何不同? (我可能遗漏了一些东西......)问“链接问题的答案真的正确吗?”很可能会将您标记为重复。
  • 通常我会避免像瘟疫这样的巧妙技巧,并认为 reinterpret_cast 充其量只是一种代码味道。 4行版本有什么问题?它很容易理解,无论如何编译器都会对其进行优化......
  • @jakub_d 链接的问题(及其答案)与在转换后操纵值(递增)有关。我的问题纯粹是关于演员的。尽管链接问题的答案似乎也回答了这个问题,但对我来说有点过于法律,因此这个问题。我也尽量避免reinterpret_cast,但例如你不能用 SIMD 和其他东西来避免它。
  • 我认为在强制转换值上调用运算符 >> 也可以称为操作。律师的东西也适用于你的案子。因此,就标准而言,它不能保证有效。

标签: c++ qt5 enum-class qdatastream


【解决方案1】:

我得到以下解决方案:

template <typename T>
typename std::enable_if<std::is_enum<T>::value, QDataStream &>::type&
operator<<(QDataStream &s, const T &t)
{ return s << static_cast<typename std::underlying_type<T>::type>(t); }

template <typename T>
typename std::enable_if<std::is_enum<T>::value, QDataStream &>::type&
operator>>(QDataStream &s, T &t)
{ return s >> reinterpret_cast<typename std::underlying_type<T>::type &>(t); }

来自github,并在src 中得到验证。

感觉人经常掉进语言的陷阱里不能自拔想出一个反例,希望方便,可以容忍可能的错误,就去做吧。否则,使用最安全的方法。

【讨论】:

    【解决方案2】:

    您可以编写一个模板函数,允许您为您定义的每个 operator&gt;&gt; 编写 1 行代码。

    template <class UT, class S, class E> S& DeserializeEnumClassValue(S &s, E &e)
    {
        UT temp;
        s >> temp;
        e = static_cast<E>(temp);
        return s;
    }
    

    然后像这样使用它:

    QDataStream &operator>>(QDataStream &stream, Type &type)
    {    
        return DeserializeEnumClassValue<char>(stream, value);
    }
    

    但这可以使用 std::underlying_type (https://en.cppreference.com/w/cpp/types/underlying_type) 进行改进,因为可以在编译时获取它。

    如果您采用这种方法,那么您还应该为operator&lt;&lt; 做类似的事情,以使维护更容易。

    【讨论】:

      猜你喜欢
      • 2017-04-10
      • 2015-07-26
      • 1970-01-01
      • 2021-11-30
      • 1970-01-01
      • 2014-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多