`pair:: operator = (pair &&)` ошибка с `auto &` выведенными операциями перемещения - регрессия libstdС++?
Учитывая эту программу:
struct Val
{
Val() = default;
Val(Val&&) = default;
auto& operator=(Val&&);
};
/* PLACEHOLDER */
auto& Val::operator=(Val&&) { return *this; }
Подставляя /* PLACEHOLDER */
с...
int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(std::begin(v), 0, Val{});
}
... успешно компилируется:
- g++ 6.2.0
- g++ 6.3.0
-
g++ 7.0.1 (trunk)
-
clang++ 3.9.1
- clang++ 5.0.0 (HEAD)
на wandbox
Подставляя /* PLACEHOLDER */
с...
template <typename TVec>
void a(TVec& v)
{
v.emplace(std::begin(v), 0, Val{});
}
int main()
{
std::vector<std::pair<int, Val>> v;
a(v);
}
... успешно компилируется:
... но создает ошибку времени компиляции:
- g++ 6.3.0
- g++ 7.0.1 (trunk)
- clang++ 5.0.0 (HEAD)
на wandbox
Полученная ошибка, по-видимому, связана с ограниченной перегрузкой pair operator=(pair&&)
- из include/bits/stl_pair.h
в GitHub libstdС++ mirror:
pair&
operator=(typename conditional<
__and_<is_move_assignable<_T1>,
is_move_assignable<_T2>>::value,
pair&&, __nonesuch&&>::type __p)
noexcept(__and_<is_nothrow_move_assignable<_T1>,
is_nothrow_move_assignable<_T2>>::value)
{
first = std::forward<first_type>(__p.first);
second = std::forward<second_type>(__p.second);
return *this;
}
-
Подстановка is_move_assignable<_T2>
с помощью std::true_type
позволяет компилировать код.
-
Перемещение определения Val::operator=(Val&&)
до /* PLACEHOLDER */
позволяет компилировать код.
-
Изменение auto& Val::operator=(Val&&)
до Val& Val::operator=(Val&&)
позволяет компилировать код.
Что здесь происходит? Является ли это дефектом реализации в последней версии libstdС++? Или старые версии неправильно компилировали плохо сформированный код?
EDIT: как AndyG, обнаруженный в его (теперь удаленном) ответе, ошибка также возникает при вызове пустой функции перед вызовом emplace
:
template <typename TVec>
void a(TVec&) { }
int main()
{
std::vector<std::pair<int, Val>> v;
a(v);
v.emplace(std::begin(v), 0, Val{});
}
Вывод a(v);
выше предотвращает создание ошибки времени компиляции. Это поведение присутствует как в g++ 7, так и в clang++ 5.
на wandbox
Другой странный случай был обнаружен Сергеем Мурзином и может быть протестирован на wandbox:
int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(v.begin(), 0, Val{});
std::cout << v.back().first << std::endl;
}
В приведенном выше коде возникает ошибка компилятора. Комментирование строки, содержащей std::cout
, предотвращает возникновение ошибки. Это поведение присутствует как в g++ 7, так и в clang++ 5.
Ответы
Ответ 1
Это почти наверняка проблема создания экземпляра. Если вы выполните что-то, что инициирует создание pair<int, Val>
определения pair<int, Val>
в main
, вы получите ошибку. В противном случае, он создается только при создании экземпляра vector::emplace
, который в настоящее время рассматривает варианты реализации до конца единицы перевода (что разрешено, см. [temp.point]/8), после чего оператор присваивания полностью определен и доступен для вызова.
a(v)
запускает экземпляр определения pair<int, Val>
, поскольку он необходим для ADL. Если вы пишете ::a(v)
или (a)(v)
(оба подавляют ADL), ошибка исчезает. (Очевидно, что a.back().first
требует создания pair<int, Val>
: вы обращаетесь к его члену данных.)