Ich versuche die "map_list_of" von boost in C ++ 03 zu verschachteln, aber die Konstruktion ist mehrdeutig?

8

Bedenken Sie Folgendes:

%Vor%

Ich wollte, dass das Ergebnis ein Programm ist, das bei der Ausführung d ausgibt.

Stattdessen bekomme ich das :

%Vor%

(ähnliche Ergebnisse unter GCC)

Wie kann ich das beheben?

Ich bekomme einen ähnlichen Fehler, auch wenn ich std::map<int, char>(map_list_of(...)) anstelle von map_list_of(...) für diese inneren Karten verwende.

    
Lightness Races in Orbit 01.05.2015, 14:00
quelle

1 Antwort

8

C ++ 03 definiert zwei Konstruktoren für map , die mit einem Argument [lib.map] p2:

aufgerufen werden können
%Vor%

boost's map_list_of erzeugt ein Objekt einer generic_list -Klassenvorlageninstanz aus dem letzten SVN:

%Vor%

Wo die primäre generic_list Vorlage den folgenden Konvertierungsoperator enthält:

%Vor%

Beide Konstruktoren map sind praktikabel, da dieser Operator die Konvertierung sowohl in map als auch in Compare erlaubt. Soweit ich weiß, kann man in C ++ 03 keinen Conversion-Operator mit SFINAE einschränken.

Das map wird explizit konstruiert, wenn ein neuer Knoten in die äußere Karte eingefügt wird. Ein Paar Iteratoren wird verwendet, um über die innere generic_list zu iterieren, um die äußere map zu konstruieren. Die Dereferenzierung dieses Iterators ergibt std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > . Der Knoten (Wert) Typ der äußeren Karte ist std::pair<int const, std::map<int, char> > .

Daher versucht der Compiler, den letzteren Typ aus dem ersteren zu konstruieren. In C ++ 03 ist dieser pair -Konstruktor nicht SFINAE-eingeschränkt, da dies in C ++ 03 nicht möglich ist. [lib.pairs] p1

%Vor%

libstdc ++ implementiert dies wie folgt:

%Vor%

Ich bin mir nicht ganz sicher, ob das konform ist, seit [lib.pairs] p4

  

Effekte: Initialisiert Member aus den entsprechenden Membern des Arguments und führt bei Bedarf implizite Konvertierungen durch.

(Aber wie gesagt, SFINAE auf ctors kann nicht in C ++ 03 implementiert werden.)

In C ++ 11 und 14 schlägt dies ebenfalls fehl, aber aus einem anderen Grund. Hier sind die Paarkonstruktoren SFINAE-beschränkt. Die Einschränkung erfordert jedoch implizite Konvertierbarkeit ( is_convertible ), während das Programm UB hat, wenn das Zielpaar der Typen nicht aus den Quellen konstruiert werden kann ( is_constructible ). Ich habe ein bisschen mehr zu diesem Thema in eine weitere SO-Antwort geschrieben. Interessanterweise ist eine vorgeschlagene Lösung N4387 zu dem darin genannten Thema andere Frage sagt:

  

Es sollte hier bemerkt werden, dass für den allgemeinen Fall der    std::is_constructible<T, U>::value Anforderung für das nicht explizite   Konstruktor, der auf std::is_convertible<U, T>::value beschränkt ist   ist nicht redundant, weil es möglich ist, Typen zu erstellen, die sein können   kopierinitialisiert, aber nicht direkt initialisiert

Dies ist genau der Fall, dem wir hier begegnen: Ein map kann von einem generic_list kopierinitialisiert werden, da dies den explicit -Konstruktor nicht lebensfähig macht. Aber ein map kann nicht direkt von generic_list initialisiert werden, da dies die Konvertierung mehrdeutig macht.

Soweit ich sehen kann, löst N4387 das Problem im OP nicht. Auf der anderen Seite haben wir mit einer einheitlichen Initialisierung eine Alternative zu map_list_of . Und wir können SFINAE Conversion-Operatoren seit C ++ 11 einschränken.

Eine Lösung besteht darin, den Konstruktor explicit zu eliminieren, indem nur implizite Konvertierungen zugelassen werden:

%Vor%

Aber es gibt einen direkteren Weg: Verwenden Sie einfach die convert_to_container -Memberfunktion von generic_list 's Basisklasse converter (auch eine Klassenvorlage):

%Vor%     
dyp 01.05.2015, 14:39
quelle

Tags und Links