`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_type
permite 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.