Spezialisierung std :: optional

8

Ist es möglich, std::optional für benutzerdefinierte Typen zu spezialisieren? Wenn nicht, ist es zu spät, dies dem Standard vorzuschlagen?

Mein Anwendungsfall dafür ist eine ganzzahlige Klasse, die einen Wert innerhalb eines Bereichs darstellt. Zum Beispiel könnten Sie eine Ganzzahl haben, die irgendwo im Bereich [0, 10] liegt. Viele meiner Anwendungen sind selbst für ein einziges Byte Overhead empfindlich, so dass ich aufgrund der zusätzlichen std::optional keine nicht spezialisierte bool verwenden könnte. Eine Spezialisierung für std::optional wäre jedoch für eine ganze Zahl, deren Bereich kleiner als der zugrunde liegende Typ ist, trivial. Wir könnten einfach den Wert 11 in meinem Beispiel speichern. Dies sollte keinen Platz- oder Zeitaufwand gegenüber einem nicht optionalen Wert bieten.

Darf ich diese Spezialisierung in namespace std erstellen?

    
David Stone 29.09.2013, 17:35
quelle

5 Antworten

8

Es gilt die allgemeine Regel in 17.6.4.2.1 [namespace.std] / 1:

  

Ein Programm kann nur dann eine Vorlagenspezialisierung für eine Standardbibliotheksvorlage zum Namespace std hinzufügen, wenn die Deklaration von einem benutzerdefinierten Typ abhängt und die Spezialisierung die Standardbibliotheksanforderungen für die ursprüngliche Vorlage erfüllt und nicht explizit ist   verboten.

Also würde ich sagen, es ist erlaubt.

N.B. optional ist nicht Bestandteil des C ++ 14-Standards, sondern wird in einer separaten technischen Spezifikation zu den Grundlagen der Bibliothek enthalten sein, so dass es Zeit gibt, die Regel zu ändern, wenn meine Interpretation falsch ist.

    
Jonathan Wakely 02.10.2013, 10:31
quelle
5

Wenn Sie nach einer Bibliothek suchen, die den Wert und die "no-value" -Flag an einem Speicherort effizient packt, empfehle ich compact_optional . Es macht genau das.

Es ist nicht auf boost::optional oder std::experimental::optional spezialisiert, aber es kann sie umschließen, so dass Sie eine einheitliche Schnittstelle erhalten, mit Optimierungen wo möglich und einem Fallback auf 'klassische' Option, falls erforderlich.

    
Andrzej 01.10.2015 06:37
quelle
2

Ich habe nach dem gleichen gefragt, was die Spezialisierung optional<bool> und optional<tribool> neben anderen Beispielen betrifft, um nur ein Byte zu verwenden. Während die "Legalität", solche Dinge zu tun, nicht diskutiert wurde, denke ich, dass es theoretisch nicht erlaubt sein sollte, optional<T> im Gegensatz zu zB .: hash (was explizit erlaubt ist) zu spezialisieren.

Ich habe die Protokolle nicht bei mir, aber ein Teil der Begründung ist, dass die Schnittstelle den Zugriff auf die Daten als Zugriff auf einen Zeiger oder Verweis behandelt, was bedeutet, dass wenn Sie eine andere Datenstruktur in den Interna verwenden, einige der Invarianten des Zugriffs können sich ändern; ganz zu schweigen von der Bereitstellung der Schnittstelle mit Zugriff auf die Daten könnte etwas wie reinterpret_cast<(some_reference_type)> erfordern. Die Verwendung von uint8_t zum Speichern eines optionalen Bool würde beispielsweise mehrere zusätzliche Anforderungen an die Schnittstelle von optional<bool> stellen, die sich von denen von optional<T> unterscheiden. Wie lautet der Rückgabetyp von operator* ?

Im Grunde glaube ich, dass die Idee darin besteht, das ganze vector<bool> fiasco wieder zu vermeiden.

In Ihrem Beispiel ist es vielleicht nicht so schlimm, da der Zugriffstyp immer your_integer_type& (oder Zeiger) ist. Aber in diesem Fall ist es vielleicht die sicherste Wahl, einfach Ihren Integer-Typ so zu gestalten, dass er einen "Zombie" oder "unbestimmten" Wert anstelle von optional<> für den Job mit seinem zusätzlichen Overhead und den zusätzlichen Anforderungen bietet.

    
Luis Machuca 29.09.2013 17:46
quelle
0

Ich sehe nicht, wie das Zulassen oder Nicht-Erlauben eines bestimmten Bitmusters, den nicht eingreifenden Zustand darzustellen, unter alles fällt, was der Standard abdeckt.

Wenn Sie versuchen, einen Bibliothekshersteller zu überzeugen, dies zu tun, würde es eine Implementierung erfordern, erschöpfende Tests, um zu zeigen, dass Sie nicht versehentlich die Anforderungen von optionalem (oder versehentlich aufgerufenem undefiniertem Verhalten) und umfassendem Benchmarking zur Anzeige gebracht haben Dies macht einen erheblichen Unterschied in realen (und nicht nur erfundenen) Situationen.

Natürlich können Sie mit Ihrem eigenen Code machen, was Sie wollen.

    
Nevin 02.10.2013 16:47
quelle
0

Erleichtern Sie sich die Platzeinsparung

Ich habe beschlossen, dass dies eine nützliche Sache ist, aber eine vollständige Spezialisierung ist ein wenig mehr Arbeit als nötig (zum Beispiel, um operator= korrekt zu bekommen).

Ich habe auf der Boost-Mailingliste eine Möglichkeit zur Vereinfachung der Aufgabe der Spezialisierung angegeben, besonders wenn Sie nur einige Instanziierungen einer Klassenvorlage spezialisieren möchten.

Ссылка

Meine aktuelle Schnittstelle beinhaltet einen speziellen Tag-Typ, der den Zugriff auf bestimmte Funktionen freigibt. Ich habe diesen Typ optional_tag genannt. Nur optional kann ein optional_tag konstruieren. Damit ein Typ sich für eine platzsparende Darstellung anmeldet, benötigt er die folgenden Elementfunktionen:

  • T(optional_tag) erstellt einen nicht initialisierten Wert
  • initialize(optional_tag, Args && ...) konstruiert ein Objekt, wenn bereits eines existiert
  • uninitialize(optional_tag) zerstört das enthaltene Objekt
  • is_initialized(optional_tag) prüft, ob sich das Objekt gerade in einem initialisierten Zustand befindet

Indem wir immer den Parameter option_tag benötigen, beschränken wir keine Funktionssignaturen. Aus diesem Grund können wir zum Beispiel operator bool() nicht als Test verwenden, da der Typ diesen Operator aus anderen Gründen benötigt.

Ein Vorteil gegenüber anderen möglichen Implementierungsmethoden ist, dass Sie mit jedem Typ arbeiten können, der einen solchen Zustand natürlich unterstützt. Es fügt keine Anforderungen wie einen Move-Konstruktor hinzu.

Sie können eine vollständige Codeimplementierung der Idee unter

sehen

Ссылка

und für eine Klasse, die die Spezialisierung verwendet:

Ссылка

(Zeilen 220 bis 242)

Ein alternativer Ansatz

Dies steht im Gegensatz zu meiner vorherigen Implementierung, bei der Benutzer eine Klassenvorlage spezialisieren mussten. Sie können die alte Version hier sehen:

Ссылка

und

Ссылка

Das Problem bei diesem Ansatz ist, dass es einfach mehr Arbeit für den Benutzer ist. Anstatt vier Elementfunktionen hinzuzufügen, muss der Benutzer in einen neuen Namespace gehen und eine Vorlage spezialisieren.

In der Praxis würden alle Spezialisierungen einen in_place_t -Konstruktor haben, der alle Argumente an den zugrunde liegenden Typ weiterleitet. Der optional_tag -Ansatz kann andererseits direkt die Konstruktoren des zugrunde liegenden Typs verwenden.

Bei der Spezialisierung optional_storage ist der Benutzer auch dafür verantwortlich, geeignete referenzenqualifizierte Überladungen einer Wertfunktion hinzuzufügen. In der optional_tag -Ansatz haben wir bereits den Wert, so dass wir es nicht herausziehen müssen.

optional_storage erforderte auch die Standardisierung als Teil der Schnittstelle von optionalen zwei Hilfsklassen, von denen nur eine der Benutzer spezialisieren sollte (und manchmal ihre Spezialisierung an die andere delegiert).

Der Unterschied zwischen diesem und compact_optional

compact_optional ist eine Art zu sagen "Behandle diesen speziellen Wächter-Wert, da der Typ nicht vorhanden ist, fast wie ein NaN". Es erfordert, dass der Benutzer weiß, dass der Typ, mit dem er arbeitet, über einen speziellen Sentinel verfügt. Ein leicht spezialisierbarer optional ist eine Art zu sagen "Mein Typ benötigt keinen zusätzlichen Speicherplatz um den nicht vorhandenen Zustand zu speichern, aber dieser Zustand ist kein normaler Wert." Es ist nicht erforderlich, dass jemand über die Optimierung Bescheid weiß, um davon profitieren zu können. Jeder, der den Typ benutzt, bekommt es kostenlos.

Die Zukunft

Mein Ziel ist es, dies zuerst in boost :: optional und dann in den optionalen std :: optional zu bringen. Bis dahin können Sie immer bounded::optional verwenden, obwohl es einige andere (beabsichtigte) Schnittstellenunterschiede gibt.

    
David Stone 03.10.2015 19:33
quelle