Der folgende Code wird mit gcc 7.1.0 mit C ++ 17-Satz kompiliert, kompiliert jedoch nicht mit C ++ 14-Satz (oder Visual Studio 2017). Es ist einfach auf Wandbox zu reproduzieren.
Was muss getan werden, damit es mit C ++ 11/14 funktioniert?
%Vor% Beginnen wir damit, warum das in C ++ nicht funktioniert 14. Es gibt zwei relevante Faktoren für std::chrono::duration
(für die std::chrono::milliseconds
Alias ist) ):
Das Vorlagenmodell passt besser zu einem Argument vom Typ Convert
. Es wird jedoch nur an der Überladungsauflösung teilnehmen, wenn Rep2
(a.k.a Convert
) implizit in den Darstellungstyp std::chrono::duration
konvertiert werden kann. Für milliseconds
, das ist long
für Wandbox. Ihr int64_t
Konvertierungsoperator ermöglicht diese implizite Konvertierung.
Aber hier ist der Haken. Bei der Überprüfung auf diese implizite Konvertierung werden die cv-Qualifikationsmerkmale der Conversion-Member-Funktion nicht berücksichtigt. Diese Überladung wird also gewählt, akzeptiert aber durch eine konstante Referenz. Und Ihr benutzerdefinierter Conversion-Operator ist nicht const
qualifiziert! @Galik hat es in den Kommentaren zu Ihrem Beitrag notiert. Daher schlägt die Konvertierung innerhalb des c'tor von milliseconds
fehl.
So, wie man es löst? Zwei Möglichkeiten:
Markieren Sie den Konvertierungsoperator const
. Das wird jedoch die Umwandlung in int64_t
für m
und i
auswählen.
Markieren Sie die Umwandlung in int64_t
als explicit
. Jetzt wird die überarbeitete Vorlage nicht mehr an der Überladungsauflösung für m
teilnehmen.
Und schließlich, warum funktioniert das in C ++ 17? Das wäre eine garantierte Kopie. Da Ihr Convert
eine Umwandlung in std::chrono::milliseconds
hat, wird es verwendet, um m
direkt zu initialisieren. Die Minutia davon beinhaltet, dass man nicht einmal den Kopierkonstruktor wählen muss, um es später zu lösen.
Das ist ein ziemlich interessanter Fall. Der Grund, warum es auf C ++ 14 nicht kompiliert wird, wird von StoryTeller korrekt erklärt und ist wohl ein LWG-Defekt - die Voraussetzung auf dem Konvertierungskonstruktor ist, dass Rep2
in rep
konvertierbar ist, aber der Körper dieses Konstruktors versucht, ein Rep2 const
in rep
zu konvertieren - und in dem bestimmten Beispiel in OP ist dies schlecht ausgebildet. Dies ist jetzt LWG 3050 .
In C ++ 17 hat sich jedoch keine der relevanten, artikulierten Regeln in der Norm geändert. Direkt-Initialisierung (zB std::chrono::milliseconds m(convert);
) berücksichtigt immer noch Konstruktoren und die beste Übereinstimmung von Die Überladungsauflösung unter den Konstruktoren wäre immer noch derselbe fehlerhafte Konvertierungskonstruktor, der bewirkt, dass das Programm nicht in C ++ 14 kompiliert werden kann.
Allerdings gibt es ein herausragendes Kernthema, das anscheinend sowohl gcc als auch clang entschieden haben zu implementieren, obwohl es noch keinen Wortlaut dafür gibt. Überlegen Sie:
%Vor% Nach den heutigen Sprachregeln ist die Kopierinitialisierung von b
in Ordnung, wir benutzen die Konvertierungsfunktion. Aber die direkte Initialisierung von b
ist nicht in Ordnung, wir müssten den gelöschten Kopierkonstruktor von A
verwenden.
Ein anderes motivierendes Beispiel ist:
%Vor% Auch hier müßten wir Cat(Cat&& )
durchlaufen - was in diesem Fall wohlgeformt ist, aber die Kopierfreiheit aufgrund von zeitweilige Materialisierung .
Die vorgeschlagene Lösung dieses Problems besteht also darin, beide -Konstruktoren und Konvertierungsfunktionen für die direkte Initialisierung zu berücksichtigen. In beiden Beispielen hier und im Beispiel des OP würde dies das "erwartete" Verhalten ergeben - die direkte Initialisierung würde nur die Konvertierungsfunktion als die bessere Übereinstimmung verwenden. Sowohl gcc als auch clang nehmen diesen Weg im C ++ 17 Modus, weshalb die Beispiele heute kompiliert werden.
Tags und Links c++ c++14 c++17 chrono conversion-operator