Kann der Konstruktor std :: vector range explizite Konvertierungen aufrufen?

14

Ist das folgende Programm gut formuliert?

#include <vector>
struct A {
    explicit A(int) {}
};
int main() {
    std::vector<int> vi = {1, 2, 3, 4, 5};
    std::vector<A> va(vi.begin(), vi.end());
}

Gemäß C ++ 17 [sequence.reqmts] ist die Anforderung für

X u(i, j);

Wo Xist ein Sequenzcontainer, ist:

Tsoll EmplaceConstructiblein Xvon sein *i.

Im vorhergehenden Absatz heißt es jedoch:

iund jbezeichnen Iteratoren, die die Anforderungen an Eingabe-Iteratoren erfüllen, und beziehen sich auf Elemente, die implizit konvertierbar sind in value_type:

Daher scheint es mir, dass beide Anforderungen erfüllt sein müssten: Der Werttyp des Bereichs muss implizit in den Werttyp des Containers konvertierbar sein und EmplaceConstructible erfüllt sein (was bedeutet, dass der Allokator in der Lage sein muss, die erforderliche Initialisierung durchzuführen). . Da intes nicht implizit konvertierbar ist, Asollte dieses Programm schlecht geformt sein.

Überraschenderweise scheint es jedoch unter GCC zu kompilieren .

Brian
quelle
(Für die Aufzeichnung ist es nicht nur gcc: godbolt.org/z/ULeRDw )
Max Langhof
In diesem Fall ist keine implizite Konvertierung erforderlich, da der explizite Konstruktor bereits zum Typ passt. Ich denke, die Beschreibung ist verwirrend, aber eine explizite Konstruktion ist immer besser als eine implizite Konvertierung vor der Konstruktion.
JHBonarius

Antworten:

2

Sequenzcontainer müssen lediglich die Konstruktion von Iteratoren unterstützen, die die Kriterien der impliziten Konvertierbarkeit erfüllen.

Dies verhindert nicht, dass Sequenzcontainer diese Konstruktion von Iteratoren unterstützen, die diese Kriterien nicht erfüllen, soweit ich das beurteilen kann 1 . Es gibt eine explizite Regel dazu:

Wenn der Konstruktor ... mit einem Typ InputIterator aufgerufen wird, der nicht als Eingabe-Iterator qualifiziert ist , darf der Konstruktor nicht an der Überlastungsauflösung teilnehmen.

Es ist unklar, was "als Eingabe-Iterator qualifizieren" genau im Kontext bedeutet. Ist es eine informelle Möglichkeit, Cpp17InputIterator auszudrücken, oder wird versucht, auf die Anforderungen von i und j zu verweisen? Ich weiß es nicht. Unabhängig davon, ob dies zulässig ist oder nicht, stellt der Standard keine strengen Anforderungen an die Erkennung:

[container.requirements.general]

Das Verhalten bestimmter Containerelementfunktionen und Abzugsleitfäden hängt davon ab, ob Typen als Eingabeiteratoren oder Zuweiser qualifiziert sind. Inwieweit eine Implementierung feststellt, dass ein Typ kein Eingabe-Iterator sein kann, ist nicht angegeben, mit der Ausnahme, dass mindestens integrale Typen nicht als Eingabe-Iteratoren gelten dürfen. ...

Mit der Interpretation, dass ein Cpp17InputIterator "als Eingabe-Iterator qualifiziert", müsste das Beispielprogramm nicht fehlerhaft sein. Aber es ist auch nicht garantiert, dass es gut geformt ist.

1 In diesem Fall kann es als Qualitätsproblem bei der Implementierung angesehen werden, wenn Sie sich darauf verlassen. Andererseits kann diese Beschränkung auf implizite Konvertierungen als Fehler angesehen werden .


PS Dies wird auch in Clang (mit libc ++) und Msvc ohne Warnungen kompiliert.

PPS Dieser Wortlaut scheint in C ++ 11 hinzugefügt worden zu sein (was natürlich ist, da damals auch explizite Konstruktoren eingeführt wurden).

Eerorika
quelle
1
Kommt wirklich darauf an, was "nicht als Eingabe-Iterator qualifiziert" bedeutet. Anders als oben heißt es nicht wirklich Cpp17InputIterator, daher ist mir nicht klar, ob "und auf Elemente verweisen, die implizit in konvertierbar sind value_type" in "Eingabe-Iterator" enthalten ist. Wenn dies der Fall ist, sollte der Konstruktor nicht an der Überlastungsauflösung teilnehmen und das Programm sollte fehlerhaft sein.
Max Langhof
1
Jede Standardbibliotheksklasse darf also zusätzliche Konstruktoren haben, ohne eine Diagnose zu stellen, wenn diese zusätzlichen Konstruktoren verwendet werden. Das scheint mir intuitiv falsch ...
Brian
@Brian Ich bin mir nicht sicher, ob es sich um "zusätzliche Konstruktoren" handelt, aber vielleicht um "spezifischere Implementierungen von Konstruktoren, die mehr Platz bieten". Das Überprüfen jeder Eingabe kann erhebliche Auswirkungen auf die Leistung haben, daher weiß ich nicht, ob dies der
richtige
@Brian Es wäre sicherlich eine schlechte Idee, auch wenn nicht ausdrücklich verboten. In diesem Fall prüfen wir nur, ob ein erforderlicher Konstruktor Iteratortypen unterstützen darf, die nicht unterstützt werden müssen. In diesem Fall gibt es eine explizite Anforderung "nicht teilnehmen", wie von Max ausgeführt, die dies sicherlich nicht zulassen würde. Es ist jedoch in der Tat unklar, was "als Eingabe-Iterator qualifizieren" genau im Kontext bedeutet. Ist es eine informelle Art, sich auszudrücken Cpp17InputIterator, oder versucht es, sich auf die Anforderungen von iund zu beziehen j? Ich weiß es nicht.
Eerorika
2
1) In C ++ setzen wir den Standard niedrig.. 2) Konstruktoren sind nicht virtuelle Elementfunktionen . 3) Siehe LWG 3297 ; Ich bin jedoch nicht besonders davon überzeugt, dass wir die implizite Konvertierungsanforderung entfernen sollten.
TC