Почему необязательная <T &> переподготовка при назначении?

Продолжается дискуссия о том, что делать optional и variant со ссылочными типами, особенно в отношении присвоения. Я хотел бы лучше понять дискуссию по этому вопросу.

optional<T&> opt;
opt = i;
opt = j; // should this rebind or do i=j?

В настоящее время решение состоит в том, чтобы сделать optional<T&> плохо сформированным и сделать variant::operator= плохо сформированным, если какой-либо из типов является ссылочным типом - чтобы обойти аргумент и предоставить нам большую часть функциональности.

Каков аргумент, что opt = j должен перепроверять базовую ссылку? Другими словами, зачем нам реализовывать optional следующим образом:

template <class T>
struct optional<T&> {
    T* ptr = nullptr;

    optional& operator=(T& rhs) {
        ptr = &rhs;
        return *this;
    }
};

Ответы

Ответ 1

Каков аргумент, который opt = j должен перепроверять базовую ссылку?

Я не знаю, что такое "аргумент", который вы ищете. Но вы только что представили для него "аргумент":

optional<T&> opt;
opt = i;
opt = j;

Теперь сделайте вид, что вторая и третья строки далеки друг от друга. Если вы просто читаете код, что бы вы ожидали opt = j? Или более того, почему вы ожидаете, что его поведение будет отличаться от opt = i?

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

Кроме того, у нас уже есть способ сообщить, что вы хотите изменить значение внутри optional. А именно: *opt = j. Это работает так же, как и для optional<T&>, как и для optional<T>.

Путь optional работает очень просто: это тип оболочки. Как и все существующие в настоящее время типы оберток, операции с ними влияют на оболочку, а не на завернутую. Чтобы повлиять на завершаемую вещь, вы явно используете * или -> или какую-либо другую функцию интерфейса.