Czy konstruktor zakresu std :: vector może wywoływać jawne konwersje?

14

Czy następujący program jest dobrze sformułowany?

#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());
}

Zgodnie z C ++ 17 [sekwencja.reqmts] wymóg dotyczący

X u(i, j);

gdzie Xjest kontenerem sekwencji, jest:

Tbędzie EmplaceConstructibledo Xz *i.

Jednak w poprzednim akapicie stwierdzono, że:

ii joznaczają iteratory spełniające wymagania iteratora wejściowego i odnoszą się do elementów domyślnie konwertowalnych value_type,

Wydaje mi się zatem, że oba wymagania musiałyby zostać spełnione: typ wartości zakresu musi być domyślnie konwertowany na typ wartości kontenera i EmplaceConstructible musi być spełniony (co oznacza, że ​​alokator musi być w stanie wykonać wymaganą inicjalizację) . Ponieważ intnie można go domyślnie przekonwertować A, ten program powinien być źle sformułowany.

Jednak, co zaskakujące, wydaje się , że kompiluje się w GCC .

Brian
źródło
(Dla przypomnienia , to nie tylko gcc: godbolt.org/z/ULeRDw )
Max Langhof
W takim przypadku nie jest wymagana niejawna konwersja, ponieważ jawny konstruktor już pasuje do tego typu. Myślę, że opis jest mylący, ale wyraźna konstrukcja jest zawsze lepsza niż niejawna konwersja przed budową.
JHBonarius

Odpowiedzi:

2

Jest to tylko wymóg, aby kontenery sekwencji wspierały konstrukcję z iteratorów, które spełniają kryteria niejawnej wymienialności.

To samo w sobie nie uniemożliwia kontenerom sekwencji wspierania tej konstrukcji z iteratorów, które nie spełniają tych kryteriów, o ile mogę powiedzieć 1 . Istnieje wyraźna zasada:

Jeśli konstruktor ... zostanie wywołany z typem InputIterator, który nie kwalifikuje się jako iterator wejściowy , wówczas konstruktor nie będzie uczestniczył w rozwiązywaniu problemów z przeciążeniem.

Nie jest jasne, co „kwalifikować się jako iterator wejściowy” oznacza dokładnie w kontekście. Czy to nieformalny sposób wyrażenia Cpp17InputIterator, czy też próbuje odwoływać się do wymagań i i j? Nie wiem Niezależnie od tego, czy jest to dozwolone, czy nie, standard nie ma ścisłego wymogu wykrywania:

[container.requirements.general]

Zachowanie niektórych funkcji członka kontenera i przewodników dedukcyjnych zależy od tego, czy typy kwalifikują się jako iteratory wejściowe, czy alokatory. Zakres, w jakim implementacja określa, że ​​typ nie może być iteratorem wejściowym, jest nieokreślony, z tym wyjątkiem, że jako minimum całkowe typy nie kwalifikują się jako iteratory wejściowe. ...

Przy interpretacji, że jakikolwiek Cpp17InputIterator „kwalifikuje się jako iterator wejściowy”, przykładowy program nie wymagałby nieprawidłowego kształtowania. Ale nie ma też gwarancji, że będzie dobrze uformowany.

1 W takim przypadku można uznać za problem z jakością wdrożenia ostrzeżenie, gdy się na nim opiera. Z drugiej strony to ograniczenie do konwersji niejawnych można uznać za wadę .


PS Kompiluje się bez ostrzeżeń w Clang (z libc ++) i Msvc.

PPS Wydaje się, że to sformułowanie zostało dodane w C ++ 11 (co jest naturalne, ponieważ wprowadzono także jawne konstruktory).

eerorika
źródło
1
Naprawdę zależy od tego, co oznacza „nie kwalifikuje się jako iterator wejściowy” . W przeciwieństwie do powyższego , tak naprawdę nie mówi Cpp17InputIterator, więc nie jest dla mnie jasne, czy „i odnoszą się do elementów domyślnie konwertowalnych na value_type jest zawarte w „iteratorze wejściowym”. Jeśli tak, to konstruktor nie powinien brać udziału w rozwiązywaniu problemów z przeciążeniem, a program powinien być źle sformułowany.
Max Langhof,
1
Tak więc każda standardowa klasa biblioteki może mieć dodatkowe konstruktory bez wydawania diagnozy, gdy te dodatkowe konstruktory są używane? To intuicyjnie wydaje mi się błędne ...
Brian
@Brian Nie jestem pewien, czy to „dodatkowe konstruktory”, ale może bardziej „konkretne implementacje konstruktorów, które pozwalają na więcej miejsca”. Sprawdzanie wszystkich danych wejściowych może mieć znaczący wpływ na wydajność, więc nie wiem, czy to byłby właściwy sposób ...
JHBonarius
@Brian Z pewnością byłby to zły pomysł, nawet jeśli nie zostałby wyraźnie zabroniony. W tym przypadku rozważamy tylko, czy wymagany konstruktor może obsługiwać typy iteratorów, które nie są wymagane do obsługi. W tym przypadku istnieje wyraźny wymóg „nie uczestniczyć”, jak wskazał Max, co z pewnością by to uniemożliwiło. Ale rzeczywiście nie jest jasne, co „kwalifikować się jako iterator wejściowy” oznacza dokładnie w kontekście. Czy jest to nieformalny sposób wyrażenia Cpp17InputIterator, czy też próbuje odwoływać się do wymagań ii j? Nie wiem
eerorika
2
1) W C ++ ustawiliśmy standard na niski. . 2) Konstruktory to nie-wirtualne funkcje składowe . 3) Patrz LWG 3297 ; nie jestem jednak szczególnie przekonany, że powinniśmy usunąć wymóg niejawnej konwersji.
TC