Учитывая эту программу:`pair :: operator = (pair &&)` ошибка с `auto &` выведенными операциями перемещения - регрессия libstdC++?
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{});
}
... успешно компилирует на:
- г ++ 6.2.0
- г ++ 6.3.0
г ++ 7.0.1 (ствол)
лязг ++ 3.9.1
- лязг ++ 5.0.0 (ГОЛОВА)
Подставив /* 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);
}
... успешно компилирует на:
- г ++ 6.2.0
- лязгом ++ 3.9.1
... но выдает ошибку во время компиляции на:
- g ++ 6.3.0
- г ++ 7.0.1 (ствол)
- лязг ++ 5.0.0 (ГОЛОВКА)
Полученная ошибка, как представляется, связано с ограничениями pair operator=(pair&&)
перегрузки - from include/bits/stl_pair.h
on GitHub's libstdc++ 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&&)
позволяет скомпилировать код.
Что здесь происходит? Является ли это дефектом реализации в последней версии libstdC++? Или старые версии неправильно компилировали плохо сформированный код?
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{});
}
Commeting из a(v);
выше, предотвращает создание ошибки времени компиляции. Это поведение присутствует как в g ++ 7, так и в clang ++ 5.
Другой странный случай был обнаружен Sergey Murzin, и может быть опробовано on 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.
[dcl.spec.auto]/11 «Если тип объекта с неопределенным типом-заполнителем необходим для определения типа выражение, программа плохо сформирована ». Поэтому я думаю, что ваша первая программа плохо сформирована. (Стандарт не говорит «никакой диагностической необходимости», поэтому должна быть диагностика). –
Несомненно, это относится к требованию, чтобы компилятор учитывал только функции, которые были определены до того, как шаблон, который он пытается создать. Может все еще быть ошибкой, поскольку я не уверен на 100% правил. – NathanOliver
Я думаю, мы могли бы спорить о том, считает ли использование _T2 в 'парном :: operator =' экземпляре как «определение типа выражения» –