2016-04-06 3 views
1

В настоящее время я пишу класс ведения журнала (только для практики) и столкнулся с проблемой. У меня есть два класса: класс Buffer действует как временный буфер и сбрасывает себя в деструктор. И класс Proxy, который возвращает экземпляр Buffer, поэтому мне не нужно писать Buffer() все время.Принудительное использование конструктора копирования/Избегайте использования конструктора копирования

В любом случае, вот код:

#include <iomanip> 
#include <iostream> 
#include <sstream> 
#include <string> 

class Buffer 
{ 
private: 
    std::stringstream buf; 
public: 
    Buffer(){}; 
    template <typename T> 
    Buffer(const T& v) 
    { 
    buf << v; 
    std::cout << "Constructor called\n"; 
    }; 
    ~Buffer() 
    { 
    std::cout << "HEADER: " << buf.str() << "\n"; 
    } 
    Buffer(const Buffer& b) 
    { 
    std::cout << "Copy-constructor called\n"; 
    // How to get rid of this? 
    }; 
    Buffer(Buffer&&) = default; 
    Buffer& operator=(const Buffer&) & = delete; 
    Buffer& operator=(Buffer&&) & = delete; 
    template <typename T> 
    Buffer& operator<<(const T& v) 
    { 
    buf << v; 
    return *this; 
    } 
}; 

class Proxy 
{ 
public: 
    Proxy(){}; 
    ~Proxy(){}; 
    Proxy(const Proxy&) = delete; 
    Proxy(Proxy&&) = delete; 
    Proxy& operator=(const Proxy&) & = delete; 
    Proxy& operator=(Proxy&&) & = delete; 
    template <typename T> 
    Buffer operator<<(const T& v) const 
    { 
    if(v < 0) 
     return Buffer(); 
    else 
     return Buffer(v); 
    } 
}; 

int main() { 
    Buffer(Buffer() << "Test") << "what"; 
    Buffer() << "This " << "works " << "fine"; 
    const Proxy pr; 
    pr << "This " << "doesn't " << "use the copy-constructor"; 
    pr << "This is a " << std::setw(10) << " test"; 
    return 0; 
} 

Здесь выход:

Copy-constructor called 
HEADER: what 
HEADER: Test 
HEADER: This works fine 
Constructor called 
HEADER: This doesn't use the copy-constructor 
Constructor called 
HEADER: This is a  test 

код делает именно то, что я хочу, но это зависит от РВО. Я прочитал несколько раз, что вы не должны полагаться на РВО, так что я хотел бы спросить, как я могу:

  1. Избегайте РВО полностью, так что конструктор копирования вызывается каждый раз, когда
  2. Избегайте конструктор копирования

Я уже пытался избежать конструктора копирования, возвращая ссылку или перемещаясь, но это segfaults. Думаю, это потому, что временный оператор Proxy :: < < удален во время возврата.

Меня также интересуют совершенно разные подходы, которые делают примерно то же самое.

+2

3. Напишите код, который не зависит от того, происходит ли RVO. – juanchopanza

+0

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

+1

Ваш класс не должен копироваться или присваиваться в любом случае (у него есть член данных 'stringstream'.) Я действительно не знаю, какую проблему вы пытаетесь решить здесь. – juanchopanza

ответ

1

Это похоже на надуманную проблему: во-первых, код работает, включен ли RVO или отключен (вы можете протестировать его, используя G ++ с the no-elide-constructors flag). Во-вторых, способ проектирования возврата объекта Buffer для использования с оператором << может быть выполнен только путем копирования : Функция Proxy::operator<<(const T& v) создает в стеке новый экземпляр Buffer, который затем удаляется при выходе из функции вызов (т. е. между каждой конкатенацией в pr << "This " << "doesn't " << "use the copy-constructor";); Вот почему вы получаете ошибку сегментации при попытке ссылаться на этот объект вне функции.

В качестве альтернативы вы можете определить оператора << для использования динамической памяти, например. возвращая unique_ptr<Buffer>:

#include <memory> 

... 

std::unique_ptr<Buffer> operator<<(const T& v) const 
{ 
    if(v < 0) 
     return std::unique_ptr<Buffer>(new Buffer()); 
    else 
     return std::unique_ptr<Buffer>(new Buffer(v)); 
} 

Однако ваши первоначальные заявления конкатенации не будет компилироваться, то, потому что Proxy::operator<<(const T& v) теперь возвращает объект типа std::unique_ptr<Buffer>, а не Buffer, что означает, что это возвращается объект не имеет собственного Proxy::operator<<(const T& v) функция определена и поэтому несколько конкатенации не будет работать без первого явно де ссылок возвращаемый указатель:

const Proxy pr; 
std::unique_ptr<Buffer> pb = pr << "This "; 
// pb << "doesn't " << "use the copy-constructor"; // This line doesn't work 
*pb << "doesn't " << "use the copy-constructor"; 

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


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

+0

Спасибо! Это очень хорошо. Я думаю, что буду придерживаться – Schore