Несобственный держатель с семантикой присваивания - PullRequest
3 голосов
/ 20 июля 2010

У меня есть класс, который должен содержать ссылку на некоторые данные, не имея этих данных (то есть фактические данные гарантированно не выйдут за рамки). В частности, , класс не может сделать копию - размер данных составляет несколько гигабайт.

Теперь обычная реализация (я полагаю) должна иметь ссылку на данные:

struct holder_ref {
    type const& value;

    holder_ref(type const& value) : value(value) { }
};

(Обратите внимание, что const ness не имеет абсолютно никакого отношения к проблеме).

Теперь мне совершенно необходимо, чтобы этот класс был назначаемым (т.е.есть рабочий operator =).Я думал, что это довольно распространенная проблема, но я не могу вспомнить, как (если вообще) я решил ее раньше.

Проблема в том, что ссылка не может быть назначена , и простоникак не обойтись.Единственное решение, которое я придумал, использует новое размещение вместо оператора присваивания:

// x = other_x; gets replaced with:
x.~T();
new (&x) T(other_x);

Теперь это работает и соответствует стандарту.Но это точно уродливо.Нет - недопустимо.

Поэтому я ищу альтернативы.Одна идея состоит в том, чтобы использовать указатели, но я не уверен, гарантированно ли работает мой конструктор (и передать указатель невозможно из-за интерфейса, которому я должен придерживаться):

struct holder_ptr {
    type const* value;

    // Is this legal?
    holder_ptr(type const& value = 0) : value(&value) { }
};

Но я 'Я бы предпочел использовать ссылку, если это вообще возможно.Только - как реализовать оператор присваивания?

struct holder_ref {
    type const& value;

    holder_ref(type const& value = 0) : value(value) { }

    holder_ref& operator =(holder_ref const& other) {
        // Now what?!
        return *this;
    }
};

В качестве контрольного примера рассмотрим следующий код:

int main() {
    int const TEST1 = 23;
    int const TEST2 = 13;
    int const TEST3 = 42;
    std::vector<holder_ptr> hptr(1);
    std::vector<holder_ref> href(2);

    // Variant 1. Pointer.
    hptr[0] = holder_ptr(TEST1);

    // Variant 2. Placement new.
    href[0].~holder_ref();
    new (&href[0]) holder_ref(TEST2);

    // Variant 3. ???
    href[1] = holder_ref(TEST3);

    assert(*hptr[0].value == TEST1);   // Works (?)
    assert(href[0].value == TEST2);    // Works
    assert(href[1].value == TEST3);    // BOOM!
}

(Кроме того, просто чтобы прояснить это - тип мы 'речь идет не о POD, и мне нужно стандартное совместимое решение.)

Ответы [ 3 ]

6 голосов
/ 20 июля 2010

Я не вижу ничего плохого в использовании holder_ptr. Это можно реализовать примерно так:

struct bad_holder : std::exception { };

struct holder_ptr {
    holder_ptr() : value(0) { }
    holder_ptr(type const& value) : value(&value) { }

    type const& get() { 
        if (value == 0) throw bad_holder();
        return *value; 
    }
private:
    type const* value;
};

До тех пор, пока вы всегда присваиваете указатель из ссылки, вы знаете, что у вас есть действительный объект (тот или у вас ранее была «нулевая ссылка», и в этом случае у вас возникают другие, более серьезные проблемы, поскольку уже будет вызывать неопределенное поведение).

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

Редактировать : я обновил пример, чтобы разрешить возможность использования держателя по умолчанию.

3 голосов
/ 20 июля 2010

Я бы использовал держатель указателя.Но если вы абсолютно против этого, как насчет сокрытия вашего места размещения? operator=:

holder_ref& operator =(holder_ref const& other) {
    new (this) holder_ref(other);
    return *this;
}
1 голос
/ 20 июля 2010

Достаточно ли соответствует стандарту TR1 слабый_птр?

...