6

所以我在 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,然后调用继承自 KxStream 的 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<< 来处理内置类型。这将使它总是更喜欢重载运算符<<'而不是 KxStream 运算符。- 重载需要一次,而 KxStream 需要两次。

4

3 回答 3

5

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

于 2012-08-13T15:05:46.583 回答
3

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

于 2012-08-13T15:07:40.090 回答
0

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

所以我做这样的事情:

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调用内置的operator<<。它只能使用 KxStream 中的公共方法。但我仍然通过私有 stream_i 方法获得虚拟方法重载。因此,当流式传输到 KxCbuf 时,编译器必须始终转换为 KxStream。这是唯一的方法。

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

于 2012-08-13T16:52:15.477 回答