Die Funktion make_size_tag
kehrt SizeTag<T>
von Wert.
Lassen Sie uns rekapitulieren, wie function returning by value Werke:
- Es gibt ein temporäres Objekt in der Regel die Rückgabewert Objekt genannt.
- Für den Fall
return expression;
:
- Der Ausdruck Objekt der Rückgabewert kopier initialisiert.
- Dies ist ein Kopie Elision Kontext.
- Für den Fall
return { zero_or_more_items };
:
- verspannten Liste copy-list-initialisiert der Rückgabewert Objekt.
- Wenn der aufrufende Code eine Variable mit dem Funktionsaufruf initialisiert, ist das Rückgabewertobjekt der Initialisierer. (Die genaue Form der Initialisierung kann abhängig vom aufrufenden Code variieren). Für die Initialisierung eines Objekts ist dies auch ein Kopie-Elisionskontext.
In Ihrem Code, folgert make_size_tag(a)
T
(der Parameter von make_size_tag
) zu int&
, weil dies ein perfektes Forwarding Szenario.
Die Instantiierung make_size_tag
für diesen T
sieht aus wie nach std::forward Ausbau aus:
SizeTag<int&> make_size_tag(int& t)
{
return t;
}
weil static_cast<int&>(t)
genauso wie t
ist, da t
ist bereits ein L-Wert vom Typ int
.
Wie bereits erwähnt, dieser Code copy-initializes der Rückgabewert Objekt. So, jetzt der Code verhält sich ein bisschen wie:
SizeTag<int&> temp_rv = t;
und weil t
ist kein SizeTag
, die Definition von kopier Initialisierung ist, dass dies ist das gleiche wie:
SizeTag<int&> temp_rv = SizeTag<int&>(t);
, die offensichtlich eine Kopie ruft/verschiebt Operation, um temp_rv
von einem temporären Typ SizeTag<int&>
zu initialisieren. Obwohl diese Kopie durch Kopieren entfernt werden würde, muss der barrierefreie Copy/Move-Konstruktor existieren.
Die Lösung von Jarod42 vorgeschlagen, Klammern um die Rückkehr Ausdruck setzen funktioniert, weil die entsprechende Initialisierung jetzt copy-list-initialization ist:
SizeTag<int&> temp_list_rv { t };
die temp_list_rv
mit dem SizeTag<int&>(int&)
Konstruktor initialisiert.
NB; Ihr Code hat einen separaten Fehler: seit Type
ist const uint64_t &
, die Initialisierung von _size
von int
erstellt eine temporäre, die zerstört wird, wenn der SizeTag
Konstruktor abgeschlossen ist; und so kehrt das Tag mit einer baumelnden Referenz zurück. clang warnt davor, aber g ++ nicht.
dies zu beheben: Sie müssen entweder Type
ändern die gleiche sein wie T&
so bindet sie direkt an a
, z.B .:
using size_type = typename std::remove_reference<T>::type;
oder _size
keine Referenz sein machen. Es scheint, dass letzteres den gesamten Zweck Ihres Tags besiegen würde, so dass Sie möglicherweise Ihr Design ein wenig überdenken müssen.
Um zu vermeiden, dass diese Danger-Referenz generiert wird, ändern Sie zu size_type &
im Conditional_t. Dann wird der Compiler (vorausgesetzt, Sie verwenden nicht MSVC) auf das Problem hinweisen.
VS2015 scheint ohne Fehler zu kompilieren. – AlexD
Welcher Compiler und wie schlägt er fehl? – luk32
@AlexD VS hat nicht die beste Erfolgsbilanz bei der Einhaltung des Standards. –