Учитывая эту программу:
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)
Подставляя /* 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.2.0
- clang++ 3.9.1
... но создает ошибку времени компиляции:
- g++ 6.3.0
- g++ 7.0.1 (trunk)
- clang++ 5.0.0 (HEAD)
Полученная ошибка, по-видимому, связана с ограниченной перегрузкой 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:
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.