【问题标题】:Problems with stream interface in C++C++中流接口的问题
【发布时间】:2012-08-13 14:54:46
【问题描述】:

所以我在 c++ 中有一个虚拟流接口

class KxStream
{
public:
   virtual KxStream& operator<< ( u32 num ) = 0;
};

它为所有内置类型提供了大量基本的

然后我有几个实现流接口的类。像这样:

class KxCbuf : public KxStream
{
public:
   KxStream& operator<<( u32 num );
}

所以在 KxCbuf 中有一个流接口的实现。到现在为止还挺好。然后我有一些重载流接口的类:

class KxSymbol
{
   operator u32() const;
   friend KxStream& operator<<( KxStream& os, KxSymbol sym );
};

请注意,此类已将运算符强制转换为内置类型。现在,当我尝试将这些类之一流式传输到实现流式接口的类之一时,我收到一个错误:

KxCbuf buf;
KxSymbol sym;

buf << sym; // error!

编译器对使用哪些函数感到困惑。 Gcc 编译得很好,但是说有多种方法可以做到这一点。 MSVC 没有编译说有多个重载:

src/variable.cpp(524) : error C2666: 'KxCbuf::operator <<' : 15 overloads have similar conversions

我知道发生了什么,只是不知道如何令人满意地解决它。所以编译器可以要么强制转换 KxCbuf -> KxStream,然后调用友元函数,这是正确的做法。或者它可以强制转换KxSymbol -> u32,然后调用KxCbuf中的u32

我可以用两种(坏的)方法解决它。

我可以从流式传输开始:

buf << "" << sym;

这样第一个流操作符的返回值“”返回KxStream,一切正常。或者我可以为实现类实现一个冗余的流操作符。例如。我可以将以下内容添加到 KxSymbol:

friend KxStream& operator<<( KxCbuf& os, KxSymbol sym );

第一个答案总是有效的 - 但它确实很难看。第二个答案也很丑陋,因为我必须创建冗余的流运算符,并且并不总是有效,因为 KxStream 实现并不总是在我需要定义新的流运算符的地方可见。

理想情况下,我希望 KxStream 接口的实现像 KxStream 对象一样工作,并避免导致模糊转换的隐式转换。

我该如何解决这个问题?

(ps。我需要为我的库的自定义序列化方案创建自己的流操作符。我不能使用具有自己的序列化类的 boost 或类似的第三方库)

@Edit:有几个很好的答案与控制编译器对隐式转换的使用有关,比如转换为 KxSymbol -> u32,不幸的是隐式转换对代码很重要。例如,KxSymbol 是一个将字符串存储在表格中并将它们作为数字返回的类,以便我可以将字符串作为数字进行比较。例如。如果两个符号不相等,则字符串不相同。我还在某些数据结构中将符号存储为数字。

有没有办法从另一方面解决这个问题,不知何故让编译器明白,应该优先将 KxStream 的实现转换为 KxStream 对象而不是其他隐式转换?

例如,如果我以某种方式强制编译器必须首先将 KxCbuf 强制转换为 KxStream,然后再将 operator

【问题讨论】:

  • 如果你通过 const 引用而不是复制传递`KxSymbol sym`会发生什么?这可能解决问题

标签: c++ serialization


【解决方案1】:

如果您有 C++11 编译器,请将转换函数标记为“显式”。这样你就不会得到到 u32 的隐式转换,这种歧义就会消失。

【讨论】:

  • 我想避免需要 C++11。这是必须在 C++11 不总是可用的地方编译的跨平台代码。
  • 那么你必须摆脱转换运算符,正如 Mark B 所建议的那样。
【解决方案2】:

最简单和最安全的方法是将隐式转换从operator u32() const; 更改为命名方法u32 as_u32() const。这不仅消除了歧义,还可以防止各种不必要的意外转换,这些转换会在将来给您带来麻烦。

【讨论】:

  • 当然,这有利于使用尚未为转换运算符实现“显式”的编译器。
【解决方案3】:

我想我可能会有答案。在虚拟私有方法中实现内置的流操作符。

所以我做了这样的事情:

class KxStream
{
public:
   KxStream& operator<< ( u32 num ) { return stream_i( num ); }
private:
   virtual KxStream& stream_i ( u32 num ) = 0;
};

class KxCbuf : public KxStream
{
private:
   KxStream& stream_i( u32 num );
}

现在编译器没有办法通过KxCbuf调用内置操作符必须始终转换到 KxStream。它是唯一的方法。

在友元重载和内置函数之间做出选择时,友元重载获胜,因为内置函数需要 2 次强制转换,而重载需要 1 次。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    • 1970-01-01
    • 2013-07-30
    • 2023-03-13
    • 2014-03-21
    • 1970-01-01
    相关资源
    最近更新 更多