Ссылка на объект с отключенным конструктором / назначением копирования

#c #c 11 #reference

#c #c 11 #ссылка

Вопрос:

В моем приложении у меня есть список объектов. Поскольку у меня должен быть только один экземпляр, я отключил как конструктор копирования, так и оператор присваивания. Перемещение объекта по-прежнему разрешено. Но поскольку я выполняю различные манипуляции с объектами, мне нужно сохранить указатель на один из них. Раньше я использовал указатель для этой цели, но теперь я хочу использовать ссылку.

По какой-то причине я не могу переназначить ссылку. Ошибка:

 error: overload resolution selected deleted operator '='
candidate function has been explicitly deleted
  

Пример кода, демонстрирующий проблему:

 #include <iostream>

class Item
{
public:
    Item() { n = (  Item::counter); }
    Item(const Itemamp;amp; other) { n = std::move(other.n); }
    Item(const Itemamp; other) = delete;
    Itemamp; operator=(const Itemamp; other) = delete;
    int Get() { return n; }
private:
    int n;
    static int counter;
};

int Item::counter = 0;

int main()
{
    Item i1;
    Item i2;

    Item *p = amp;i1;
    printf("%dn", p->Get());

    p = amp;i2;
    printf("%dn", p->Get());

    Item amp;r = i1;
    printf("%dn", r.Get());

    r = i2; // here I get the error
    printf("%dn", r.Get());

    return 0;
}
  

Хорошо, я могу понять, если получу ошибку при чем-то подобном:

 Item i3 = i2;
  

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

Итак, мой вопрос в том, как я могу сохранить ссылку на не скопированный объект, избегая указателей?

Комментарии:

1. «По какой-то причине я не могу переназначить ссылку». Причина в том, что язык этого не допускает.

2. Если вы ожидали повторной привязки этой ссылки к другому объекту, язык работает не так. После привязки при инициализации required ссылка не может быть привязана (но ее можно оставить висящей, если вы не будете осторожны). В вашем коде r = i2; является синонимом i1 = i2; , и вы обнаружите, что это тоже не будет компилироваться.

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

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

5. Очевидно, что у вас может быть ссылка. Вы его создали. с Item amp;r = i1; помощью But вы не можете повторно привязать эту ссылку к другому объекту, как вы можете с помощью указателя. Язык работает не так. И, кстати, ваша оценка Item i3 = i2; как «в случае действительно присваивания» — тоже неточна. Это не присваивание, это инициализация копирования .

Ответ №1:

В C ссылки не могут быть повторно привязаны. Что это значит? Это означает, что после создания ссылки вы не можете изменить объект, на который ссылается эта ссылка. Это имеет серьезные последствия — например, этот код:

 Item i1;
Item i2;
Item amp;r = i1;
r = i2; 
  

логически эквивалентно этому:

 Item i1;
Item i2;
i1 = i2; 
  

Теперь должно быть понятно, почему у компилятора проблемы с оператором присваивания. Что нужно сделать? Вы можете использовать std::reference_wrapper, который вам нужен — повторно привязываемый ссылочный тип:

 Item i1;
Item i2;
auto r = std::ref(i1);
r = std::ref(i2); 
  

Комментарии:

1. Спасибо @bartop, это отличное решение! К сожалению, я ничего об этом не знал.