2017-01-31 3 views
18

dieses Programm Gegeben:`pair :: operator = (Paar &&)` Fehler mit `auto &` abgeleitete Verschiebeoperationen - libstdC++ Regression?

struct Val 
{ 
    Val() = default; 
    Val(Val&&) = default; 
    auto& operator=(Val&&); 
}; 

/* PLACEHOLDER */ 

auto& Val::operator=(Val&&) { return *this; } 

/* PLACEHOLDER */ mit ...

int main() 
{ 
    std::vector<std::pair<int, Val>> v; 
    v.emplace(std::begin(v), 0, Val{}); 
} 

... kompiliert erfolgreich auf Substituieren:

  • g ++ 6.2.0
  • g ++ 6.3.0
  • g ++ 7.0.1 (Stamm)

  • Klirren ++ 3.9.1

  • Klirren ++ 5.0.0 (HEAD)

on wandbox


Substituieren /* PLACEHOLDER */ mit ...

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); 
} 

... kompiliert erfolgreich auf:

  • g ++ 6.2.0
  • Klirren ++ 3.9.1

... aber erzeugt einen Fehler bei der Kompilierung auf:

  • g ++ 6.3.0
  • g ++ 7.0.1 (trunk)
  • Klirren ++ 5.0.0 (HEAD)

on wandbox


Der erzeugte Fehler scheint zu einer eingeschränkten pair operator=(pair&&) Überlastung bezogen werden - 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; 
    } 
  • Das Ersetzen von durch std::true_type ermöglicht die Kompilierung des Codes.

  • Durch Verschieben der Definition von Val::operator=(Val&&) vor /* PLACEHOLDER */ kann der Code kompiliert werden.

  • Durch die Änderung auto& Val::operator=(Val&&) zu Val& Val::operator=(Val&&) kann der Code kompiliert werden.

Was geht hier vor? Ist das ein Implementierungsfehler in der neuesten Version von libstdC++? Oder haben die älteren Versionen schlecht formatierten Code falsch kompiliert?


EDIT: als AndyG in seiner entdeckt (jetzt gelöscht) Antwort, auch der Fehler tritt auf, wenn ein Anruf auf eine leere Funktion gemacht wird emplace vor dem Aufruf:

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); Commeting aus oben verhindert, dass der Kompilierungsfehler erzeugt wird. Dieses Verhalten ist sowohl in g ++ 7 als auch in clang ++ 5 vorhanden.

on wandbox


Eine andere seltsame Fall von Sergey Murzin entdeckt wurde, und kann on wandbox getestet werden:

int main() 
{ 
    std::vector<std::pair<int, Val>> v; 
    v.emplace(v.begin(), 0, Val{}); 
    std::cout << v.back().first << std::endl; 
} 

Der obige Code erzeugt einen Compiler-Fehler. Das Auskommentieren der Zeile, die std::cout enthält, verhindert, dass der Fehler auftritt. Dieses Verhalten ist sowohl in g ++ 7 als auch in clang ++ 5 vorhanden.

+2

[dcl.spec.auto]/11 "Wenn der Typ einer Entität mit einem nichtdeducated Platzhaltertyp benötigt wird, um den Typ eines zu bestimmen Ausdruck, das Programm ist schlecht geformt. " Also ich denke, dein erstes Programm ist schlecht geformt. (Der Standard sagt nicht "keine Diagnose erforderlich", so dass es eine Diagnose geben sollte). –

+1

Ziemlich sicher, dass dies unter der Anforderung fällt, dass der Compiler nur Funktionen berücksichtigt, die vor der Vorlage definiert wurden, die es zu instanziieren versucht. Könnte immer noch ein Fehler sein, da ich mir bei den Regeln nicht 100% ig sicher bin. – NathanOliver

+0

Ich denke, wir könnten darüber streiten, ob die Verwendung von _T2 in 'pair :: operator = 'Instanziierung als" den Typ eines Ausdrucks bestimmen " –

Antwort

9

Dies ist fast sicher ein Punkt der Instanziierung Problem. Wenn Sie etwas tun, das die Instanziierung der pair<int, Val> Definition in main auslöst, dann erhalten Sie den Fehler. Ansonsten wird es nur instanziiert, wenn instanziiert wird, was die hier in Rede stehenden Implementierungen auf das Ende der Übersetzungseinheit verschieben (was erlaubt ist, siehe [temp.point]/8), zu welchem ​​Zeitpunkt der Zuweisungsoperator vollständig definiert und aufrufbar ist.

a(v) löst die Instanziierung der Definition von pair<int, Val> aus, weil sie für ADL benötigt wird. Wenn Sie ::a(v) oder (a)(v) schreiben (beides unterdrückt ADL), verschwindet der Fehler. (Offensichtlich erfordert a.back().first die Instanziierung von : Sie greifen auf sein Datenelement zu.)

Verwandte Themen