Почему 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 шт):
В стандарте языка этот оператор объявлен именно явно. Если посмотреть примечание к данному оператору, то там сказано
Remarks: This operator is defined as deleted unless
is_copy_assignable_v<first_type>istrueandis_copy_assignable_v<second_type>istrue.
То есть аргументы шаблона должны удовлетворять требованиям 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;
}
Дополнительно нужно "навесить" проверок времени компиляции, как и сделано в приведенном в вопросе примере.
Для того чтобы оператор присваивания мог работать даже с типами, инстанциированными ссылками.
#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, то не будет работать, а сейчас работает.