`pair:: operator=(pair &&)` error con `auto &` operaciones de movimiento deducidas-¿regresión de libstdc++?
c++14 language-lawyer (1)
Esto es casi ciertamente un problema de punto de instanciación. Si haces algo que activa la creación de instancias de la definición de pair<int, Val> en main , entonces obtienes el error. De lo contrario, solo se crea una instancia cuando se crea una instancia de vector::emplace , que las implementaciones en cuestión aquí difieren hasta el final de la unidad de traducción (que está permitida, consulte [temp.point]/8 ), en cuyo punto el operador de asignación está completamente Definido y llamable.
a(v) activa la creación de instancias de la definición de pair<int, Val> porque es necesaria para ADL. Si escribe ::a(v) o (a)(v) (ambos suprimen ADL), el error desaparecerá. (Obviamente, a.back().first requiere la creación de instancias del pair<int, Val> : está accediendo a su miembro de datos).
Dado este programa:
struct Val
{
Val() = default;
Val(Val&&) = default;
auto& operator=(Val&&);
};
/* PLACEHOLDER */
auto& Val::operator=(Val&&) { return *this; }
Sustituyendo /* PLACEHOLDER */ con ...
int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(std::begin(v), 0, Val{});
}
... compila exitosamente en:
- g ++ 6.2.0
- g ++ 6.3.0
g ++ 7.0.1 (troncal)
clang ++ 3.9.1
- clang ++ 5.0.0 (HEAD)
Sustituyendo /* PLACEHOLDER */ con ...
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);
}
... compila exitosamente en:
- g ++ 6.2.0
- clang ++ 3.9.1
... pero produce un error en tiempo de compilación en:
- g ++ 6.3.0
- g ++ 7.0.1 (troncal)
- clang ++ 5.0.0 (HEAD)
El error producido parece estar relacionado con una sobrecarga del pair operator=(pair&&) restringido pair operator=(pair&&) - desde include/bits/stl_pair.h en el espejo libstdc ++ de GitHub :
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;
}
Sustituir
is_move_assignable<_T2>constd::true_typepermite que se compile el código.Mover la definición de
Val::operator=(Val&&)antes de/* PLACEHOLDER */permite compilar el código.Cambiar
auto& Val::operator=(Val&&)aVal& Val::operator=(Val&&)permite que el código se compile.
¿Que esta pasando aqui? ¿Es este un defecto de implementación en la última versión de libstdc ++? ¿O las versiones anteriores compilaban incorrectamente el código mal formado?
EDITAR: como descubrió AndyG en su respuesta (ahora eliminada) , el error también ocurre cuando se realiza una llamada a una función vacía antes de invocar 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{});
}
Completando a(v); lo anterior evita que se produzca el error en tiempo de compilación. Este comportamiento está presente tanto en g ++ 7 como en clang ++ 5.
Otro caso extraño fue descubierto por Sergey Murzin , y puede ser probado en wandbox :
int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(v.begin(), 0, Val{});
std::cout << v.back().first << std::endl;
}
El código anterior produce un error de compilación. Comentando la línea que contiene std::cout evita que ocurra el error. Este comportamiento está presente tanto en g ++ 7 como en clang ++ 5.