std :: make_unique's (und emplace, emplace_back's) peinliche Ableitung für initializer_list Argumente

8

Sagen wir, ich habe diese Struktur:

%Vor%

und eine andere Klasse, die dies als Konstruktorargument verwendet:

%Vor%

Wie kann ich das einfache

bekommen? %Vor%

um zu arbeiten?

Gegenwärtig versucht der Compiler, über die initializer_list<int> übereinzustimmen und die Array-Variante von make_unique aufzurufen, was albern ist, weil positioned nur einen Konstruktor hat. Dasselbe Problem tritt für die Funktionen emplace und emplace_back auf. So ziemlich jede Funktion, die ihre variablen Template-Argumente an den Konstruktor einer Klasse weiterleitet, scheint dieses Verhalten zu zeigen.

Ich verstehe, dass ich das durch

lösen kann
  1. Geben Sie positioned einen Konstruktor mit zwei int -Argumenten und den {} im Aufruf von make_unique oder
  2. ab
  3. spezifiziert explizit den Typ des Arguments für make_unique als position{1,2} .

Beide scheinen übermäßig wortreich zu sein, wie es mir scheint (mit einigem Aufwand in der make_unique-Implementierung), das kann ohne diese Überspezifizierung des Argumenttyps gelöst werden.

Ist das ein lösbarer Fehler in der make_unique -Implementierung oder ist das ein unlösbarer, uninteressanter Edge-Case, den niemandem interessieren sollte?

    
rubenvb 31.12.2017, 15:11
quelle

3 Antworten

7

Die Argumentableitung von Funktionsvorlagen funktioniert nicht, wenn eine stained-init-Liste angegeben wird. Es funktioniert nur basierend auf tatsächlichen Ausdrücken.

Es sollte auch beachtet werden, dass positioned auf keinen Fall von {1, 2} initialisiert werden kann. Dies wird versuchen, einen Konstruktor mit zwei Argumenten aufzurufen, und positioned hat keinen solchen Konstruktor. Sie müssten positioned({1, 2}) oder positioned{{1, 2}} verwenden.

Als solche wäre die allgemeine Lösung, dass make_unique irgendwie die Signatur aller möglichen Konstruktoren für den Typ, den sie konstruiert, magisch reproduziert. Dies ist offensichtlich in C ++ zu diesem Zeitpunkt keine vernünftige Sache.

Eine Alternative wäre, ein Lambda zum Erstellen des Objekts zu verwenden und eine alternative make -Funktion zu schreiben. Verwenden Sie die garantierten Elisionsregeln von C ++ 17, um den zurückgegebenen prvalue auf den internen new -Ausdruck anzuwenden:

%Vor%

Sie können sogar den Parameter typename T ablegen:

%Vor%     
Nicol Bolas 31.12.2017 17:59
quelle
1

Soweit ich sehen kann, ist der praktischste Weg, dies zu tun, wahrscheinlich, die Klammern zu entfernen und Konstruktoren hinzuzufügen, um die Argumente diskret zu nehmen:

%Vor%

Wenn position mehr als ein ctor hatte, würden Sie wahrscheinlich eine variadic template ctor für positioned erstellen, um einige beliebige Parameter zu übernehmen und an position s ctor (s) weiterzuleiten.

%Vor%

Auf diese Weise werden die Argumente von make_unique nach positioned nach position weitergeleitet. Dies bietet zumindest einen gewissen potentiellen Vorteil in der Effizienz - anstatt die Argumente zu verwenden, um ein temporäres Objekt zu erzeugen, das dann übergeben wird, um das zugrundeliegende Objekt zu initialisieren, übergibt es (Referenzen auf) die ursprünglichen Objekte direkt an das ctor für das zugrundeliegendes Objekt, also konstruieren wir es nur einmal, an Ort und Stelle.

Beachten Sie, dass dies uns eine gewisse Vielseitigkeit verleiht. Angenommen, positioned war selbst eine Vorlage und das zugrunde liegende position ein Vorlagenargument:

%Vor%

Kompatibilität: Ich glaube, das erfordert C ++ 14 oder neuer, da% make_unique dann seine Weiterleitung / variadic ctor hat.

    
Jerry Coffin 31.12.2017 17:54
quelle
0

Das Problem ist, dass Initiatoren wie {1, 2} nicht abgeleitet werden können: der folgende Code funktioniert nicht (aus gutem Grund: Wie ist {1, 2} ?).

%Vor%

make_unique ist nur eine komplexere Variante dieses Themas.

Die Ursache wird hier im Detail erklärt: initializer_list und Vorlagentyp Abzug

    
Jean-Michaël Celerier 31.12.2017 15:57
quelle