перегрузите оператор сдвига ‹‹, чтобы разрешить цепочку

Я борюсь с перегрузкой оператора, так как хочу, чтобы он разрешал цепочку

class A{
 int a;
public:
 void doSomething(char *str);
 A operator<<(char *str);
}

Итак, у меня есть этот класс, и я могу взять строку и сделать что-то, что не важно для этого вопроса.

Что я сейчас мог сделать, так это

A *counter = new A();
counter->doSomething("hello");

Если я реализую перегруженный оператор сдвига

A A::operator<<(char *str)
{
  this->doSomething(str);
  return this;
}

Я могу написать это так

A *counter = new A();
(*counter) << "hello";

Надеюсь, я не ошибся здесь, потому что теперь мне интересно, как я мог разрешить цепочку

(*counter) << "hello" << "test";

Я знаю, что с цепочкой это сделает это

(*counter).operator<<("hello" << "test");

что явно не имеет никакого смысла, поскольку есть два массива строк/символов. Но я знаю, что есть способ сделать это. Я гуглил, но каждый вопрос касался только объединения экземпляров одного типа.

то, что я тогда пытался, заключалось в том, чтобы поместить два аргумента в функцию и добавить ее в качестве друга ... Я не уверен, но, возможно, мне нужно создать новый перегруженный оператор с типом char* или потоковым объектом и сделать его другом A ?

Спасибо, что помогли мне, я думаю, должен быть простой ответ, которого я сейчас не вижу.


person raemaerne    schedule 22.07.2013    source источник
comment
Вашему оператору лучше взять const char*.   -  person Tony Delroy    schedule 22.07.2013


Ответы (4)


Вам нужно вернуть ссылку на *this, поэтому тип возвращаемого значения должен быть A&:

A& operator<<(char *str)
{
  this->doSomething(str); // as before
  return *this;           // return reference to this instance.
}
person juanchopanza    schedule 22.07.2013
comment
Ну, вы можете вернуться по значению и цепочке вызовов. Но вы можете объяснить, почему вы обычно хочете возвращаться по ссылке. - person jrok; 22.07.2013

В вашем вопросе есть два неверных предположения, которые я хочу прояснить.

  1. Если вы хотите вызывать operator<< для указателей, вы должны делать это для каждого вызова, а не только для первого. Вот почему

    A *counter = new A();
    (*counter) << "hello";
    

    работает, но

    (*counter) << "hello" << "test";
    

    нет. Второй operator<< будет вызываться по указателю, возвращенному первым, а это не то, что вам нужно. Вы должны были написать (что некрасиво):

    (*((*counter) << "hello")) << "test";
    
  2. #P5#
    A << "hello" << "test";
    
    #P6#
    A.operator<<("hello" << "test");
    
    #P7#
    A.operator<<("hello").operator<<("test");
    
    #P8#
    ( A.operator<<("hello") ).operator<<("test");
    

Цепочка операторов возможна, если объект, возвращаемый operator<<, также реализует operator<<, поэтому вы можете вернуть копию *this типа A. Однако это приведет к копированию объекта, который не нужен, поскольку он, скорее всего, будет жить очень временно и будет использоваться только один раз следующим оператором.

Итак, что вы хотите сделать, это вернуть ссылку в operator<<, по которой может быть вызван следующий operator<<. Это позволяет избежать копирования:

A& operator<<(char *str)
{
    this->doSomething(str);
    return *this;
}

Возврат указателя возможен, но не приведет к тому, что вы хотите, как объяснялось выше. Синтаксис был бы очень уродливым.

person leemes    schedule 22.07.2013
comment
+1 - хорошее объяснение. Re Когда вы хотите вызывать operator<< для указателей, вы должны делать это для каждого вызова, а не только для первого. - это верно, когда operator<< является членом, но T* operator<<(T*, const char*), не являющийся членом, также можно представить, если это странно.... - person Tony Delroy; 22.07.2013
comment
Да, это возможно, но действительно странно. Я бы не советовал перегружать операторы для любого типа указателей слева. Просто потому, что это выглядит странно. - person leemes; 23.07.2013

operator << должен возвращать ссылку для цепочки.

A& operator<<(char *str);

A& A::operator<<(char *str)
{
  this->doSomething(str);
  return *this;
}

Следует использовать как

A counter;
counter << "hello" << "test";
person ForEveR    schedule 22.07.2013

A *counter = new A();

делает ваш "счетчик" указателем, а не объектом. Чтобы использовать оператор на A, вы можете использовать

(*counter) << "hello" 

Кроме того, я бы посоветовал вам еще раз подумать о перегрузке операторов. Хотя поначалу это часто кажется классной идеей, она может легко привести к коду, который трудно понять и поддерживать.

person ecotax    schedule 22.07.2013
comment
(*A) << "hello" должно быть (*counter) << "hello". - person ForEveR; 22.07.2013