Почему std::pair имеет пользовательский оператор присваивания, а не использует default версию?

Почему std::pair имеет пользовательский оператор присваивания, а не использует default версию?

Код из libc++ просто делает почленное присваивание, что и сделал бы дефолтный оператор:

_LIBCPP_INLINE_VISIBILITY
pair& operator=(typename conditional<
                    is_copy_assignable<first_type>::value &&
                    is_copy_assignable<second_type>::value,
                pair, __nat>::type const& __p)
    _NOEXCEPT_(is_nothrow_copy_assignable<first_type>::value &&
               is_nothrow_copy_assignable<second_type>::value)
{
    first = __p.first;
    second = __p.second;
    return *this;
}

Копирующий конструктор, например, использует дефолтную реализацию, так что это не code style:

pair(pair const&) = default;

Ответы (2 шт):

Автор решения: Croessmah

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

Remarks: This operator is defined as deleted unless is_­copy_­assignable_­v<first_­type> is true and is_­copy_­assignable_­v<second_­type> is true.

То есть аргументы шаблона должны удовлетворять требованиям std::is_­copy_­assignable. std::is_­copy_­assignable требует, чтобы первый аргумент мог присваиваться из константного второго: std::is_assignable<T&, const T&>.

В свою очередь, std::is_assignable должен давать true, если выражение std::declval<T>() = std::declval<U>() является компилируемым, т.е. объекту типа T можно присвоить объект типа U.

Т.е. следующий код не должен компилироваться:

std::pair<int[4], int> p1;
std::pair<int[4], int> p2;
p2 = p1;//Должна быть ошибка

Так вот, если оставить оператор по-умолчанию, то ошибки не будет. Давайте сделаем имитацию на собственном классе:

template<typename T>
struct tester
{    
    tester& operator=(tester const &) = default;
    T mem;
};

tester<int[4]> p1;
tester<int[4]> p2;
p2 = p1;//Нет ошибки
using type = int[4];
static_assert(std::is_copy_assignable_v<type>, "");//Ошибка

Как видим, ошибка отсутствует, хотя static_assert показывает, что тип int[4] не удовлетворяет требованиям is_copy_assignable.

Поэтому, неявно генерируемый оператор присваивания не подходит.

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

tester& operator=(tester const & rhv)
{
    mem = rhv.mem;
    return *this;
}

Дополнительно нужно "навесить" проверок времени компиляции, как и сделано в приведенном в вопросе примере.

→ Ссылка
Автор решения: user397900

Для того чтобы оператор присваивания мог работать даже с типами, инстанциированными ссылками.

https://godbolt.org/z/jq648q

#include <iostream>

template <class T1, class T2>
struct pair {
    T1 first;
    T2 second;

    // pair& operator=(const pair&) = default; // 1

    pair& operator=(const pair& rhs) { // 2
        first = rhs.first;
        second = rhs.second;
        return *this;
    }
};

int main() {
    int x = 1, y = 2;
    pair<int&, int> p1{x, y}, p2{y, x};
    p1 = p2;
    std::cout << p1.first << p1.second << p2.first << p2.second << '\n';
}

Если тут закомментировать 2 и раскомментировать 1, то не будет работать, а сейчас работает.

→ Ссылка