Оператор перегрузки << - PullRequest
       16

Оператор перегрузки <<

4 голосов
/ 02 марта 2011

Я делаю простой класс, который использует operator<<.Он будет хранить два параллельных массива данных, каждый с различным (но уже известным) типом данных.Идея заключается в том, что конечный интерфейс будет выглядеть примерно так:

MyInstance << "First text" << 1 << "Second text" << 2 << "Third text" << 3;

, что позволило бы массивам выглядеть примерно так:

StringArray: | "First text" | "Second text" | "Third text" |
IntArray:    | 1            | 2             | 3            |

Я могу справиться с логикой проверки вводачтобы убедиться, что все совпадает, но я запутался с техническими подробностями operator<<.

Учебники, которые я проверял, говорят, чтобы перегрузить его как функцию друга с типом возврата std::ostream&, но мой классне имеет ничего общего с потоками.Я попытался использовать void в качестве возвращаемого типа, но получил ошибки компиляции.В конце концов я вернул ссылку на класс, но я не уверен, почему это работает.

Вот мой код:

class MyClass
{
public:

MyClass& operator<<(std::string StringData)
{
    std::cout << "In string operator<< with " << StringData << "." << std::endl;

    return *this; // Why am I returning a reference to the class...?
}

MyClass& operator<<(int IntData)
{
    std::cout << "In int operator<< with " << IntData << "." << std::endl;

    return *this;
}
};

int main()
{   
MyClass MyInstance;
MyInstance << "First text" << 1 << "Second text" << 2 << "Third text" << 3;

return 0;
}

Кроме того, пользователь моегоКласс может сделать что-то вроде этого, что нежелательно:

MyInstance << "First text" << 1 << 2 << "Second text" << "Third text" << 3;

Что я могу сделать, чтобы обеспечить переменную природу ввода?

Ответы [ 3 ]

7 голосов
/ 02 марта 2011

Причина, по которой операторы ostream возвращают ссылку на ostream, и причина, по которой это несколько помогло вашему случаю вернуть ссылку на MyClass, заключается в том, что выражение типа A << B << C всегда интерпретируется как (A << B) << C. То есть все, что возвращает первый перегруженный оператор, становится левой частью следующего вызова оператора.

Теперь, если вы хотите, чтобы выражение типа MyInstance << "First text" << 1 << 2 вызывало ошибку компилятора, вам нужно убедиться, что тип, возвращаемый после первых двух операторов <<, является типом, который нельзя вызвать с другим int. Я думаю, что-то подобное может сделать то, что вы хотите (спасибо @Pete Kirkham за хорошую идею по улучшению):

struct MyClass_ExpectInt;
class MyClass {
private:
    friend MyClass& operator<<(const MyClass_ExpectInt&, int);
    void insert_data(const std::string& StringData, int IntData);
    // ...
};
struct MyClass_ExpectInt {
    MyClass& obj_ref;
    std::string str_data;
    explicit MyClass_ExpectInt( MyClass& obj, const std::string& str )
      : obj_ref( obj ), str_data( str ) {}
};
MyClass_ExpectInt operator<<( MyClass& obj, const std::string& StringData )
{
    // Do nothing until we have both a string and an int...
    return MyClass_ExpectInt( obj, StringData );
}
MyClass& operator<<( const MyClass_ExpectInt& helper, int IntData )
{
    helper.obj_ref.insert_data( helper.str_data, IntData );
    return helper.obj_ref;
}

Это будут только две перегруженные функции operator<<, которые вы определяете, связанные с MyClass. Таким образом, каждый раз, когда вы вызываете operator<<, компилятор переключает тип возвращаемого значения с MyClass& на MyClass_ExpectInt или наоборот, и передача «неправильных» данных в operator<< никогда не допускается.

2 голосов
/ 02 марта 2011
MyInstance << "First text" << 1;

Эта линия вызывает operator<<(operator<<(MyInstance, "First text"), 1).Если бы operator<< не вернул ссылку на MyClass, «внешний» вызов не состоялся бы, так как вы бы пропустили void там, где ожидается MyClass &.

Для принудительного применения чередующегосяТипы времени компиляции, вам нужно создать вспомогательный класс, например, MyClassHelper.Затем вам нужно создать эти два оператора:

MyClassHelper & operator<<(MyClass &, std::string const &);
MyClass & operator<<(MyClassHelper &, int);

Затем каждый оператор должен возвращать ссылку на «другой» вовлеченный объект.Это гарантирует, что после << "string" возвращаемая ссылка будет MyClassHelper, которая имеет только оператор << для int.И после <code><< int возвращаемая ссылка - это MyClass, у которого есть только оператор << для строки </p>

0 голосов
/ 02 марта 2011

В дополнение к тому, что должен сказать планировщик: синтаксис

x << "one" << 1 << "two" << 2;

Не показывает, что «один» принадлежит 1. Сегодня вам это приятно, но объяснить будет очень сложнозавтра кому-то еще, и еще труднее будет перепроектировать его сопровождающим через два года.Это только потому, что напоминает «обычный» оператор вставки, который поддерживает поддержку большего количества типов.

Если у вас есть свобода выбора, как будет выглядеть ваш APIЛучше сделайте что-нибудь сейчас, чтобы прояснить, что вы действительно вставляете два связанных значения.

Это можно сделать, например, разрешив вставку только std::pair<string,int>:

x << make_pair( "one", 1 ) 
  << make_pair( "two", 2 )
  ;
...