Итак, у меня есть виртуальный потоковый интерфейс на С++
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, а затем вызвать оператор u32 << в KxCbuf, унаследованном от KxStream.
Я могу решить ее двумя (плохими )способами.
Я могу начать с потоковой передачи чего-то однозначного:
buf << "" << sym;
Таким образом, возвращаемое значение оператора первого потока для "" возвращает KxStream, и все в порядке. Или я могу реализовать избыточный оператор потока для класса реализации. Например. Я могу добавить следующее в KxSymbol:
friend KxStream& operator<<( KxCbuf& os, KxSymbol sym );
Первый ответ всегда работает -, но он уродлив. Второй ответ тоже некрасивый, в том, что мне приходится создавать избыточные операторы потока, и не всегда работает в том смысле, что реализации KxStream не всегда видны в местах, где мне нужно определить новые операторы потока.
В идеале я хотел бы, чтобы реализации интерфейса KxStream работали точно так же, как объекты KxStream, и избегали неявных приведений, вызывающих неоднозначные преобразования.
Как мне это решить?
(пс. Мне нужно создать свои собственные операторы потоковой передачи для пользовательской схемы сериализации для моей библиотеки. Я не могу использовать boost или аналогичные сторонние библиотеки, у которых есть собственные классы сериализации )
. @Edit :Есть несколько хороших ответов, связанных с контролем использования компилятором неявного преобразования, такого как преобразование в KxSymbol -> u32, к сожалению, это неявное преобразование важно для кода. Например, KxSymbol — это класс, который хранит строки в таблице и возвращает их в виде чисел, чтобы я мог сравнивать строки как числа. Например. если два символа не равны, то и строки не совпадают. Я также храню символы как числа в некоторых структурах данных.
Есть ли способ решить это с другой стороны,каким-то образом заставить компилятор понять, что реализации KxStream следует приводить к объектам KxStream, а не другим неявным приведениям?
Например, что, если я каким-то образом заставлю компилятор сначала преобразовать KxCbuf в KxStream, прежде чем использовать оператор<< для встроенных типов -. Это заставит его всегда предпочитать операторы перегрузки<< операторам KxStream. -для перегрузок потребуется одно приведение, а для KxStream — два.