C ++ - Konvertierungsoperator nach chrono :: duration - arbeitet mit C ++ 17, aber nicht mit C ++ 14 oder weniger

8

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%     
Slav Slavov 21.01.2018, 10:07
quelle

2 Antworten

9

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) ):

%Vor%

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:

  1. Markieren Sie den Konvertierungsoperator const . Das wird jedoch die Umwandlung in int64_t für m und i auswählen.

  2. 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.

    
StoryTeller 21.01.2018, 10:42
quelle
7

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.

    
Barry 22.01.2018 15:21
quelle